【NOIP 2024 T2】遗失的赋值
NOIP 专栏:NOIP 2024 T2
算法竞赛:组合数学、数学
题目链接:洛谷 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 ≤ 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 n 个变量 x i , i ∈ [ 1 , n ] x_i,i\in [1,n] xi,i∈[1,n],其取值范围为 { y ∣ 1 ≤ y ≤ v , y ∈ Z } \{y | 1\le y\le v,y\in \mathbb{Z}\} {y∣1≤y≤v,y∈Z},变量的取值要满足 n − 1 n-1 n−1 个二元限制和 m m m 个一元限制,限制如下:
- a i , b i , i ∈ [ 1 , n − 1 ] a_i,b_i,i\in [1,n-1] ai,bi,i∈[1,n−1] 为二元限制参数,取值范围为 { y ∣ 1 ≤ y ≤ v , y ∈ Z } \{y | 1\le y\le v,y\in \mathbb{Z}\} {y∣1≤y≤v,y∈Z},限制为:如果 x i = a i x_i=a_i xi=ai 则要求 x i + 1 = b i x_{i+1}=b_i xi+1=bi。
- c j , d j , j ∈ [ 1 , m ] c_j,d_j,j\in [1,m] cj,dj,j∈[1,m] 为一元限制参数,取值范围为 { y ∣ 1 ≤ y ≤ v , y ∈ Z } \{y | 1\le y\le v,y\in \mathbb{Z}\} {y∣1≤y≤v,y∈Z},限制为: x c j = d j x_{c_j}=d_j xcj=dj。
现在已知 m m m 组一元限制的参数 c j , d j c_j,d_j cj,dj,而对于 n − 1 n-1 n−1 组二元限制参数 a i , b i a_i,b_i ai,bi 是未知的,现要求出对于参数 a i , b i , i ∈ [ 1 , n − 1 ] a_i,b_i,i\in [1,n-1] ai,bi,i∈[1,n−1] 取值的组合的方案数,每种组合要使得至少存在一种给每个变量 x i x_i xi 赋值的方案是满足所有限制的(一元、二元限制),方案数对 1 0 9 + 7 10^9+7 109+7 取模。
题目分析
根据题目所述,容易知道:
- 如果一元限制之间有冲突,则方案数为 0 0 0,即当 i ≠ j i\ne j i=j 时,若存在 c i = c j c_i=c_j ci=cj,而 d i ≠ d j d_i\ne d_j di=dj,则方案数为 a n s = 0 ans=0 ans=0。
- 对于一对连续的变量 x i , x i + 1 x_i,x_{i+1} xi,xi+1,如果两者均没有一元限制,那么 a i , b i a_i,b_i ai,bi 的取值组合方案数为 v 2 v^2 v2,进而对于一个长度为 n n n 且没有一元限制的变量 x i , i ∈ [ 1 , n ] x_i,i\in [1,n] xi,i∈[1,n] 序列,由乘法原理可知,参数 a i , b i , i ∈ [ 1 , n − 1 ] a_i,b_i,i\in [1,n-1] ai,bi,i∈[1,n−1] 满足要求的组合方案数为 a n s = v 2 ( n − 1 ) ans=v^{2(n-1)} ans=v2(n−1)。
- 对于一个长度为 n n n 且仅左端点有一元限制的变量 x i , i ∈ [ 1 , n ] x_i,i\in [1,n] xi,i∈[1,n] 序列,其方案数也为 a n s = v 2 ( n − 1 ) ans=v^{2(n-1)} ans=v2(n−1),因为左端点有一元限制,就相当于确定左端点变量的值,而二元限制是对于前一个数满足条件则限制后一个数,所以仅左端点存在一元限制并无实际作用意义。
- 对于一个长度为 n n n 且仅右端点有一元限制的变量 x i , i ∈ [ 1 , n ] x_i,i\in [1,n] xi,i∈[1,n] 序列,其方案数也为 a n s = v 2 ( n − 1 ) ans=v^{2(n-1)} ans=v2(n−1),与仅左端点存在一元限制情况同理,可以从右向左来确定二元限制,这样就相当于仅左端点存在一元限制。
题目思路
Way of Thinking \large \textit{\textbf {Way of Thinking}} Way of Thinking
对于一个长度为 n n n 且仅左右端点有一元限制的变量 x i , x 2 , x 3 , ⋯ , x n − 2 , x n − 1 , x n x_i,x_2,x_3,\cdots,x_{n-2},x_{n-1},x_n xi,x2,x3,⋯,xn−2,xn−1,xn 序列,存在 k k k 个二元限制( k = n − 1 k=n-1 k=n−1),设其方案数为 f ( k ) f(k) f(k)。
- 对于
x
1
,
x
2
x_1,x_2
x1,x2 之间的二元限制
(
a
1
,
b
1
)
(a_1,b_1)
(a1,b1) 如果使
a
1
=
x
1
a_1=x_1
a1=x1,则要
x
2
=
b
2
x_2=b_2
x2=b2,此时
a
1
a_1
a1
存在一种方案,而 b 1 b_1 b1 有 v v v 种方案,故第一对二元限制 ( a 1 , b 1 ) (a_1,b_1) (a1,b1) 有 v v v 种方案,这时相当于给 x 2 x_2 x2
添加了一元限制,故序列的方案数为 v × f ( k − 1 ) v\times f(k-1) v×f(k−1)。 - 如果使 a 1 ≠ x 1 a_1\ne x_1 a1=x1,这样 a 1 a_1 a1 存在 v − 1 v-1 v−1 种方案, b 1 b_1 b1 有 v v v 种方案,故第一对二元限制 ( a 1 , b 1 ) (a_1,b_1) (a1,b1) 有 v × ( v − 1 ) v\times (v-1) v×(v−1) 种方案,这时对于 i ≥ 2 i\ge 2 i≥2 的 x i x_i xi 序列相当于变成了一个仅右端点存在一元限制的变量序列 x 2 , x 3 , ⋯ , x n − 2 , x n − 1 , x n x_2,x_3,\cdots,x_{n-2},x_{n-1},x_n x2,x3,⋯,xn−2,xn−1,xn,对于这个序列方案数有 v 2 ( k − 1 ) v^{2(k-1)} v2(k−1) 种,故这种情况的方案数为 v × ( v − 1 ) × v 2 ( k − 1 ) v\times (v-1)\times v^{2(k-1)} v×(v−1)×v2(k−1),即 v 2 k − v 2 k − 1 v^{2k}-v^{2k-1} v2k−v2k−1。
特殊的,对于 k k k 取值的边界,当 k = 1 k=1 k=1 时, f ( x ) = v × ( v − 1 ) + 1 = v 2 − v + 1 f(x)=v\times (v-1)+1=v^2-v+1 f(x)=v×(v−1)+1=v2−v+1。
综上所述, f ( k ) = v 2 k − v 2 k − 1 + v × f ( k − 1 ) f(k)=v^{2k}-v^{2k-1}+v\times f(k-1) f(k)=v2k−v2k−1+v×f(k−1)。这样就有了一个方案数的递推式,然而如果递推地求解时间复杂度有些高,那么试一试能否将 f ( k ) f(k) f(k) 变成一个关于 k k k 的一个函数( v v v 作为常量)。
将 f ( k ) f(k) f(k) 展开:
f ( k ) = v 2 k − v 2 k − 1 + v × f ( k − 1 ) = v 2 k − v 2 k − 1 + v × ( v 2 x − 2 − v 2 x − 3 + v × f ( k − 2 ) ) = v 2 k − v 2 k − 1 + v 2 x − 1 − v 2 x − 2 + v 2 × f ( k − 2 ) = v 2 k − v 2 x − 2 + v 2 × f ( k − 2 ) = v 2 k − v 2 k − 2 + v 2 × ( v 2 x − 4 − v 2 x − 5 + v × f ( k − 3 ) ) = v 2 k − v 2 k − 2 + v 2 x − 2 − v 2 x − 3 + v 3 × f ( k − 3 ) = v 2 k − v 2 x − 3 + v 3 × f ( k − 3 ) = ⋯ ⋯ \begin{align} f(k) & = v^{2k}-v^{2k-1}+v\times f(k-1) \\ & = v^{2k}-v^{2k-1}+v\times (v^{2x-2}-v^{2x-3}+v\times f(k-2)) \\ & = v^{2k}-v^{2k-1}+v^{2x-1}-v^{2x-2}+v^2\times f(k-2) \\ & = v^{2k}-v^{2x-2}+v^2\times f(k-2) \\ & = v^{2k}-v^{2k-2}+v^2\times (v^{2x-4}-v^{2x-5}+v\times f(k-3)) \\ & = v^{2k}-v^{2k-2}+v^{2x-2}-v^{2x-3}+v^3\times f(k-3) \\ & = v^{2k}-v^{2x-3}+v^3\times f(k-3) \\ & = \cdots \hspace{0mm} \cdots \\ \end{align} f(k)=v2k−v2k−1+v×f(k−1)=v2k−v2k−1+v×(v2x−2−v2x−3+v×f(k−2))=v2k−v2k−1+v2x−1−v2x−2+v2×f(k−2)=v2k−v2x−2+v2×f(k−2)=v2k−v2k−2+v2×(v2x−4−v2x−5+v×f(k−3))=v2k−v2k−2+v2x−2−v2x−3+v3×f(k−3)=v2k−v2x−3+v3×f(k−3)=⋯⋯
由 l i n e ( 4 ) , l i n e ( 7 ) line(4),line(7) line(4),line(7) 行可以得出 f ( k ) f(k) f(k) 与 f ( k − t ) f(k-t) f(k−t) 的关系:
f ( k ) = v 2 k − v 2 k − t + v t × f ( k − t ) f(k)=v^{2k}-v^{2k-t}+v^t\times f(k-t) f(k)=v2k−v2k−t+vt×f(k−t)
为消去 t t t,将 t = k − 1 t=k-1 t=k−1 代入得:
f ( k ) = v 2 k − v 2 k − t + v t × f ( k − t ) = v 2 k − v 2 k − ( k − 1 ) + v k − 1 × f ( k − ( k − 1 ) ) = v 2 k − v k + 1 + v k − 1 × f ( 1 ) = v 2 k − v k + 1 + v k − 1 × ( v 2 − v + 1 ) = v 2 k − v k + 1 + v k + 1 − v k + v k − 1 = v 2 k − v k + v k − 1 \begin{align} f(k) & = v^{2k}-v^{2k-t}+v^t\times f(k-t) \\ & = v^{2k}-v^{2k-(k-1)}+v^{k-1}\times f(k-(k-1)) \\ & = v^{2k}-v^{k+1}+v^{k-1}\times f(1) \\ & = v^{2k}-v^{k+1}+v^{k-1}\times (v^2-v+1) \\ & = v^{2k}-v^{k+1}+v^{k+1}-v^k+v^{k-1} \\ & = v^{2k}-v^k+v^{k-1} \\ \end{align} f(k)=v2k−v2k−t+vt×f(k−t)=v2k−v2k−(k−1)+vk−1×f(k−(k−1))=v2k−vk+1+vk−1×f(1)=v2k−vk+1+vk−1×(v2−v+1)=v2k−vk+1+vk+1−vk+vk−1=v2k−vk+vk−1
故得知:
f ( k ) = v 2 k − v k + v k − 1 f(k)=v^{2k}-v^k+v^{k-1} f(k)=v2k−vk+vk−1
题目做法
Specific Solution \large \textit{\textbf {Specific Solution}} Specific Solution
对于每组测试数据:
- 读入并存储 m m m 个一元限制,判断一元限制之间是否冲突(若冲突则输出 0 0 0 并进行下一轮数据);
- 将一元限制按 c i , i ∈ [ 1 , m ] c_i,i\in [1,m] ci,i∈[1,m] 升序排序并去重,得到 c i , i ∈ [ 1 , m ′ ] c_i,i\in [1,m'] ci,i∈[1,m′];
- 计算变量序列 x [ 1 ∼ c 1 ] x[1\sim c_1] x[1∼c1] 的方案数 v 2 ( c 1 − 1 ) v^{2(c_1-1)} v2(c1−1) 和 x [ c m ′ ∼ n ] x[c_{m'}\sim n] x[cm′∼n] 的方案数 v 2 ( n − c m ′ ) v^{2(n-c_{m'})} v2(n−cm′),根据乘法原理将两者相乘得到 a n s ans ans;
- 遍历序列 c [ 1 ∼ m ′ ] c[1\sim m'] c[1∼m′],对于每个 c i , i ∈ [ 1 , m ′ − 1 ] c_i,i\in [1,m'-1] ci,i∈[1,m′−1],使 a n s = a n s × f ( c i + 1 − c i ) ans=ans\times f(c_{i+1}-c_i) ans=ans×f(ci+1−ci),得到最终方案数 a n s ans ans。
注意:
- 在公式 f ( k ) = v 2 k − v k + v k − 1 f(k)=v^{2k}-v^k+v^{k-1} f(k)=v2k−vk+vk−1 中存在含有模运算的减法,在取模之前需要加上模数(这很重要)。
- 计算 v k v^k vk 时应使用快速幂来求解以保证时间效率和取模正确性。
- 可以使用 m a p map map 容器来存储 c i , d i c_i,d_i ci,di,使用 m a p map map 同时不必在排序与去重,且判断冲突和顺序便利也能很方便的实现,但是对于每组测试数据要记得清空容器(建议在每组程序开始时清空,这样不容易遗漏情况;也可以在每组程序开始前创建容器,这样便不需要清空容器)。
参考代码
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
typedef long long LL;
map<int,int>mp;
int pows(int a,int k)
{
LL ans=1;
for (; k; k>>=1,a=(LL)a*a%mod)
if (k&1) ans=(LL)ans*a%mod;
return ans;
}
int main()
{
int t,fl;
scanf("%d",&t);
while (t--)
{
fl=0;
int n,m,v;
LL ans=1;
scanf("%d%d%d",&n,&m,&v);
for (int i=0; i<m; i++)
{
int x,y;
scanf("%d%d",&x,&y);
if (mp[x]&&mp[x]!=y) fl=1;
mp[x]=y;
}
if (fl)
{
puts("0");
mp.clear();
continue;
}
auto last=mp.begin();
ans=pows(v,last->first-1<<1);
for (auto it=mp.begin(); it!=mp.end(); it++)
{
if (it==last) continue;
int l=last->first,r=it->first;
int num=((pows(v,r-l<<1)-pows(v,r-l)+mod)%mod+pows(v,r-l-1))%mod;
ans=ans*num%mod;
last=it;
}
ans=ans*pows(v,n-last->first<<1)%mod;
printf("%lld\n",ans);
mp.clear();
}
return 0;
}
END
感谢观看,如有问题欢迎指出。
Experience \large \textit{\textbf {Experience}} Experience
注意到公式 f ( k ) = v 2 k − v k + v k − 1 f(k)=v^{2k}-v^k+v^{k-1} f(k)=v2k−vk+vk−1,减数是 v k v^k vk,而被减数是 v 2 k v^{2k} v2k,显然 v 2 k > v k v^{2k}>v^k v2k>vk,然而:




很显然,经过模运算,并不存在 v 2 k % m o d > v k % m o d v^{2k}\%mod>v^k\%mod v2k%mod>vk%mod 的大小关系。
更新日志
- 2025/09/21 开始书写本篇 优快云 博客,并完稿发布。
- 2025/10/12 对本篇 优快云 进行了微调与修改,并开启了本题目专栏项目(致力于多种解法)。
NOIP2024遗失赋值解法
1181

被折叠的 条评论
为什么被折叠?



