【组合数学】P11362 [NOIP2024] 遗失的赋值|普及+
本文涉及知识点
组合数学汇总
P11362 [NOIP2024] 遗失的赋值
题目描述
小 F 有 n n n 个变量 x 1 , x 2 , … , x n x_1, x_2, \ldots , x_n x1,x2,…,xn。每个变量可以取 1 1 1 至 v v v 的整数取值。
小 F 在这 n n n 个变量之间添加了 n − 1 n - 1 n−1 条二元限制,其中第 i i i( 1 ≤ i ≤ n − 1 1 \leq i \leq n - 1 1≤i≤n−1)条限制为:若 x i = a i x_i = a_i xi=ai,则要求 x i + 1 = b i x_{i+1} = b_i xi+1=bi,且 a i a_i ai 与 b i b_i bi 为 1 1 1 到 v v v 之间的整数;当 x i ≠ a i x_i \neq a_i xi=ai 时,第 i i i 条限制对 x i + 1 x_{i+1} xi+1 的值不做任何约束。除此之外,小 F 还添加了 m m m 条一元限制,其中第 j j j( 1 ≤ j ≤ m 1 \leq j \leq m 1≤j≤m)条限制为: x c j = d j x_{c_j} = d_j xcj=dj。
小 F 记住了所有 c j c_j cj 和 d j d_j dj 的值,但把所有 a i a_i ai 和 b i b_i bi 的值都忘了。同时小 F 知道:存在给每一个变量赋值的方案同时满足所有这些限制。
现在小 F 想知道,有多少种 a i , b i a_i, b_i ai,bi( 1 ≤ i ≤ n − 1 1 \leq i \leq n - 1 1≤i≤n−1)取值的组合,使得能够确保至少存在一种给每个变量 x i x_i xi 赋值的方案可以同时满足所有限制。由于方案数可能很大,小 F 只需要你输出方案数对 1 0 9 + 7 10^9 + 7 109+7 取模的结果。
输入格式
本题包含多组测试数据。
输入的第一行包含一个整数 T T T,表示测试数据的组数。
接下来包含 T T T 组数据,每组数据的格式如下:
第一行包含三个整数 n , m , v n, m, v n,m,v,分别表示变量个数、一元限制个数和变量的取值上限。
接下来 m m m 行,第 j j j 行包含两个整数 c j , d j c_j, d_j cj,dj,描述一个一元限制。
输出格式
对于每组测试数据输出一行,包含一个整数,表示方案数对 1 0 9 + 7 10^9 + 7 109+7 取模的结果。
输入输出样例 #1
输入 #1
3
2 1 2
1 1
2 2 2
1 1
2 2
2 2 2
1 1
1 2
输出 #1
4
3
0
说明/提示
【样例 1 解释】
- 对于第一组测试数据,所有可能的 ( a 1 , b 1 ) (a_1, b_1) (a1,b1) 取值的组合 ( 1 , 1 ) , ( 1 , 2 ) , ( 2 , 1 ) , ( 2 , 2 ) (1, 1), (1, 2), (2, 1), (2, 2) (1,1),(1,2),(2,1),(2,2) 都满足限制。例如, ( a 1 , b 1 ) = ( 1 , 1 ) (a_1, b_1) = (1, 1) (a1,b1)=(1,1) 时, ( x 1 , x 2 ) = ( 1 , 1 ) (x_1, x_2) = (1, 1) (x1,x2)=(1,1) 满足所有限制,而 ( a 1 , b 1 ) = ( 2 , 2 ) (a_1, b_1) = (2, 2) (a1,b1)=(2,2) 时, ( x 1 , x 2 ) = ( 1 , 1 ) (x_1, x_2) = (1, 1) (x1,x2)=(1,1) 与 ( x 1 , x 2 ) = ( 1 , 2 ) (x_1, x_2) = (1, 2) (x1,x2)=(1,2) 均满足所有限制。
- 对于第二组测试数据,只有 ( x 1 , x 2 ) = ( 1 , 2 ) (x_1, x_2) = (1, 2) (x1,x2)=(1,2) 一种可能的变量赋值,因此只有 ( a 1 , b 1 ) = ( 1 , 1 ) (a_1, b_1) = (1, 1) (a1,b1)=(1,1) 不满足限制,其余三种赋值均满足限制。
- 对于第三组测试数据,不存在一种变量赋值同时满足 x 1 = 1 x_1 = 1 x1=1 和 x 1 = 2 x_1 = 2 x1=2,因此也不存在满足限制的 ( a 1 , b 1 ) (a_1, b_1) (a1,b1)。
【样例 2】
见选手目录下的 assign/assign2.in
与 assign/assign2.ans
。
该样例共有 10 10 10 组测试数据,其中第 i i i( 1 ≤ i ≤ 10 1 \leq i \leq 10 1≤i≤10)组测试数据满足数据范围中描述的测试点 i i i 的限制。
【样例 3】
见选手目录下的 assign/assign3.in
与 assign/assign3.ans
。
该样例共有 10 10 10 组测试数据,其中第 i i i( 1 ≤ i ≤ 10 1 \leq i \leq 10 1≤i≤10)组测试数据满足数据范围中描述的测试点 i + 10 i + 10 i+10 的限制。
【数据范围】
对于所有的测试数据,保证:
- 1 ≤ T ≤ 10 1 \leq T \leq 10 1≤T≤10,
- 1 ≤ n ≤ 1 0 9 1 \leq n \leq 10^9 1≤n≤109, 1 ≤ m ≤ 1 0 5 1 \leq m \leq 10^5 1≤m≤105, 2 ≤ v ≤ 1 0 9 2 \leq v \leq 10^9 2≤v≤109,
- 对于任意的 j j j( 1 ≤ j ≤ m 1 \leq j \leq m 1≤j≤m),都有 1 ≤ c j ≤ n 1 \leq c_j \leq n 1≤cj≤n, 1 ≤ d j ≤ v 1 \leq d_j \leq v 1≤dj≤v。
测试点 | n ≤ n \leq n≤ | m ≤ m \leq m≤ | v ≤ v \leq v≤ | 特殊性质 |
---|---|---|---|---|
1 , 2 1, 2 1,2 | 6 6 6 | 6 6 6 | 2 2 2 | 无 |
3 3 3 | 9 9 9 | 9 9 9 | 2 2 2 | 无 |
4 , 5 4, 5 4,5 | 12 12 12 | 12 12 12 | 2 2 2 | 无 |
6 6 6 | 1 0 3 10^3 103 | 1 1 1 | 1 0 3 10^3 103 | 无 |
7 7 7 | 1 0 5 10^5 105 | 1 1 1 | 1 0 5 10^5 105 | 无 |
8 , 9 8,9 8,9 | 1 0 9 10^9 109 | 1 1 1 | 1 0 9 10^9 109 | 无 |
10 10 10 | 1 0 3 10^3 103 | 1 0 3 10^3 103 | 1 0 3 10^3 103 | A |
11 11 11 | 1 0 4 10^4 104 | 1 0 4 10^4 104 | 1 0 4 10^4 104 | A |
12 12 12 | 1 0 5 10^5 105 | 1 0 5 10^5 105 | 1 0 5 10^5 105 | A |
13 13 13 | 1 0 4 10^4 104 | 1 0 3 10^3 103 | 1 0 4 10^4 104 | B |
14 14 14 | 1 0 6 10^6 106 | 1 0 4 10^4 104 | 1 0 6 10^6 106 | B |
15 , 16 15, 16 15,16 | 1 0 9 10^9 109 | 1 0 5 10^5 105 | 1 0 9 10^9 109 | B |
17 17 17 | 1 0 4 10^4 104 | 1 0 3 10^3 103 | 1 0 4 10^4 104 | 无 |
18 18 18 | 1 0 6 10^6 106 | 1 0 4 10^4 104 | 1 0 6 10^6 106 | 无 |
19 , 20 19, 20 19,20 | 1 0 9 10^9 109 | 1 0 5 10^5 105 | 1 0 9 10^9 109 | 无 |
特殊性质 A:保证 m = n m = n m=n,且对于任意的 j j j( 1 ≤ j ≤ m 1 \leq j \leq m 1≤j≤m),都有 c j = j c_j = j cj=j。
特殊性质 B:保证 d j = 1 d_j = 1 dj=1。
组合数学
预处理
下标改成从0开始。
v={ c i , d i c_i,d_i ci,di}排序去重,如果相邻的两项 c i c_i ci相同直接返回0。
令v[i]={e_i,f_i}
0 ≤ j < e 0 , a j 和 b j 0 \le j< e_0,a_j和b_j 0≤j<e0,aj和bj可以任意选择。共 v 2 × e 0 v^{2\times e0} v2×e0
e b a c k ≤ j < N − 1 , a j 和 b j e_{back} \le j <N-1,a_j和b_j eback≤j<N−1,aj和bj可以任意选择。共 v 2 × ( N − 1 − e b a c k ) v^{2\times (N-1-e_back)} v2×(N−1−eback)
∀ e i ≤ j < e i + 1 \forall e_i \le j < e_{i+1} ∀ei≤j<ei+1 共有 x = e i + 1 − e i , c 0 = v 2 × x x=e_{i+1}-e_i,c0=v^{2 \times x} x=ei+1−ei,c0=v2×x种选择。
发生冲突的方案c1 = v x − 1 ( v − 1 ) =v^{x-1}(v-1) =vx−1(v−1)
冲突的方案: a e i = f i , b e i = a e i + 1 = 任意 ⋯ b e i + 1 − 1 ≠ f e i + 1 a_{e_i}=f_i,b_{e_i}=a_{e_i+1}=任意 \cdots b_{e_{i+1}-1} \neq f_{e_{i+1}} aei=fi,bei=aei+1=任意⋯bei+1−1=fei+1
合法方案=c0-c1。
所有区域相乘便是答案。
代码
核心代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>#include <bitset>
#include <chrono>
using namespace std::chrono;
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T1, class T2, class T3, class T4, class T5 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4, T5>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t) ;return in;
}template<class T1, class T2, class T3, class T4, class T5, class T6 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4, T5, T6>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t) >> get<5>(t) ;return in;
}template<class T1, class T2, class T3, class T4, class T5, class T6, class T7 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4, T5, T6, T7>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t) >> get<5>(t) >> get<6>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;AuotToFile();}void writestr(const char* sz) {strcpy(m_p, sz);m_p += strlen(sz);AuotToFile();}inline void write(char ch){*m_p++ = ch;AuotToFile();}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);m_p = puffer;}~COutBuff() {ToFile();}
private:inline void AuotToFile() {if (m_p - puffer > N - 100) {ToFile();}}char puffer[N], * m_p;
};template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行 return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错 }m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};template<long long MOD = 1000000007, class T1 = int, class T2 = long long>
class C1097Int
{
public:C1097Int(T1 iData = 0) :m_iData(iData% MOD){}C1097Int(T2 llData) :m_iData(llData% MOD) {}C1097Int operator+(const C1097Int& o)const{return C1097Int(((T2)m_iData + o.m_iData) % MOD);}C1097Int& operator+=(const C1097Int& o){m_iData = ((T2)m_iData + o.m_iData) % MOD;return *this;}C1097Int& operator-=(const C1097Int& o){m_iData = ((T2)MOD + m_iData - o.m_iData) % MOD;return *this;}C1097Int operator-(const C1097Int& o)const{return C1097Int(((T2)MOD + m_iData - o.m_iData) % MOD);}C1097Int operator*(const C1097Int& o)const{return((T2)m_iData * o.m_iData) % MOD;}C1097Int& operator*=(const C1097Int& o){m_iData = ((T2)m_iData * o.m_iData) % MOD;return *this;}C1097Int operator/(const C1097Int& o)const{return *this * o.PowNegative1();}C1097Int& operator/=(const C1097Int& o){*this *= o.PowNegative1();return *this;}bool operator==(const C1097Int& o)const{return m_iData == o.m_iData;}bool operator<(const C1097Int& o)const{return m_iData < o.m_iData;}C1097Int pow(T2 n)const{C1097Int iRet = (T1)1, iCur = *this;while (n){if (n & 1){iRet *= iCur;}iCur *= iCur;n >>= 1;}return iRet;}C1097Int PowNegative1()const{return pow(MOD - 2);}T1 ToInt()const{return ((T2)m_iData + MOD) % MOD;}
private:T1 m_iData = 0;;
};typedef C1097Int<> BI;
class Solution {
public:int Ans(const int N, const int v, vector<pair<int, int>>& cd) {sort(cd.begin(), cd.end());cd.erase(unique(cd.begin(), cd.end()), cd.end());for (auto& [c, d] : cd) { c--; }for (int i = 1; i < cd.size(); i++) {if (cd[i].first == cd[i - 1].first) { return 0; }}BI ans = BI(v).pow(2 * cd[0].first);ans *= BI(v).pow(2 * (N - 1 - cd.back().first));for (int i = 0; i + 1 < cd.size(); i++) {const int x = cd[i + 1].first - cd[i].first;ans *= (BI(v).pow(2 * x) - BI(v).pow(x - 1) * (v - 1));}return ans.ToInt();}
};
int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG ios::sync_with_stdio(0); cin.tie(nullptr);//CInBuff<> in; COutBuff<10'000'000> ob;int T;cin >> T;while(T--){ int n, m, v;cin >> n >> m >> v;auto cd = Read<pair<int, int>>(m);
#ifdef _DEBUG printf("N=%d,v=%d", n,v);Out(cd, ",cd=");//Out(B, ",B=");//Out(que, ",que=");
#endif // DEBUG Solution slu;auto res = Solution().Ans(n,v,cd);cout << res << "\n";}return 0;
}
单元测试
vector<pair<int, int>> cd;TEST_METHOD(TestMethod11){N = 2, v = 2, cd = { {1,1} };auto res = Solution().Ans(N,v,cd);AssertEx(4, res);}TEST_METHOD(TestMethod12){N = 2, v = 2, cd = { {1,1},{2,2} };auto res = Solution().Ans(N, v, cd);AssertEx(3, res);}TEST_METHOD(TestMethod13){N = 2, v = 2, cd = { {1,1},{1,2} };auto res = Solution().Ans(N, v, cd);AssertEx(0, res);}
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。