CF1895F Fancy Arrays
题意
定义一个长度为 n n n 的非负整数序列是 Fancy 的,当且仅当:
- ∃ i ∈ [ 1 , n ] \exist i\in [1, n] ∃i∈[1,n], a i ∈ [ x , x + k − 1 ] a_i\in[x, x + k - 1] ai∈[x,x+k−1]。
- ∀ i ∈ [ 2 , n ] \forall i\in [2, n] ∀i∈[2,n], ∣ a i − a i − 1 ∣ ≤ k |a_i - a_{i - 1}| \leq k ∣ai−ai−1∣≤k。
1 ≤ n , k ≤ 1 0 9 1\leq n,k\leq 10^9 1≤n,k≤109, 0 ≤ x ≤ 40 0\leq x\leq 40 0≤x≤40。
多测,给定 n , x , k n,x,k n,x,k,求有多少 Fancy 序列,答案对 1 0 9 + 7 10^9 + 7 109+7 取模。
解析
题意就是让你求出有多少个长度为 n n n 的数组,使得相邻两个数相差不超过 k k k,并且至少有一个数属于 [ x , x + k − 1 ] [x,x+k-1] [x,x+k−1]。
我们先看 “并且至少有一个数属于 [ x , x + k − 1 ] [x,x+k-1] [x,x+k−1]” 这一条件,需要满足这一条件,就需要同时满足 max i = 1 n a i ≥ x \max_{i=1}^na_i\ge x maxi=1nai≥x 且 min i = 1 n a i ≤ x + k − 1 \min_{i=1}^{n}a_i\le x+k-1 mini=1nai≤x+k−1。
用容斥原理转化一下,我们就可以,用 min i = 1 n a i ≤ x + k − 1 \min_{i=1}^{n}a_i\le x+k-1 mini=1nai≤x+k−1 的方案总数减去 max i = 1 n a i < x \max_{i=1}^na_i\lt x maxi=1nai<x 的方案总数。
(I) 看 min i = 1 n a i ≤ x + k − 1 \min_{i=1}^{n}a_i\le x+k-1 mini=1nai≤x+k−1的方案总数如何求
我们发现只要确定了 min i = 1 n a i \min_{i=1}^{n}a_i mini=1nai 和 a a a 的差分数组 c c c,就可以唯一确定数组 a a a。
因为 min i = 1 n a i \min_{i=1}^{n}a_i mini=1nai 要小于等于 x + k − 1 x+k-1 x+k−1 这个区间中所以有 x + k x+k x+k 中方案选择。因为 ∀ c i \forall c_i ∀ci 都需要在区间 [ − k , k ] [-k,k] [−k,k] 中 ( ∣ c i ∣ ≤ k |c_i|\le k ∣ci∣≤k),所以有 2 k + 1 2k+1 2k+1 种选择,差分数组上有 n − 1 n-1 n−1 个位置,所以有 ( 2 k + 1 ) n − 1 (2k+1)^{n-1} (2k+1)n−1 种选择。根据简单的配列组合知识,就可以得到第一部分的总方案数是 ( x + k ) × ( 2 k + 1 ) n − 1 (x+k)\times (2k+1)^{n-1} (x+k)×(2k+1)n−1。
(II) 看 max i = 1 n a i < x \max_{i=1}^na_i\lt x maxi=1nai<x 的方案总数如何求
后者就是要求出 ∀ a i ∈ [ 0 , x ) \forall a_i\in [0,x) ∀ai∈[0,x) 的方案数,可以发现可以用 DP 求出。
令 f i , j f_{i,j} fi,j 表示 a i = j a_i=j ai=j 的总方案个数,可以得到转移方程 f i , j = ∑ t = j − k j + k f i − 1 , t f_{i,j}=\sum_{t=j-k}^{j+k}f_{i-1,t} fi,j=∑t=j−kj+kfi−1,t,但是题目 n n n 的数据范围太大并且 x x x 的范围很小,又发现 f f f 的转移式恒定不变,考虑矩阵快速幂优化,具体方法如下:
定义初始一个 1 ∗ x 1*x 1∗x的矩阵 F \text{F} F 以及一个 x ∗ x x*x x∗x 转移矩阵 G \text{G} G,其中 F \text{F} F的初始值都是 1 1 1, G \text{G} G 的初始值若 ∣ i − j ∣ ≤ k |i-j|\le k ∣i−j∣≤k 则令 G i , j = 1 G_{i,j}=1 Gi,j=1, 最后求求 n n n 次幂即可。
得到了两部分的答案,减一下就可以了,时间复杂度 O ( x 3 log 2 n ) O(x^3\log_{2}n) O(x3log2n)。
附代码:
#include<bits/stdc++.h>
#define INF 1e16
#define endl '\n'
#define PI acos(-1)
#define pb push_back
#define int long long
#define mem(x,v) memset(x,v,sizeof x)
using namespace std;
const int N = 45;
const int MOD = 1e9+7;
inline int read(){
register int x=0,f=1;register char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int T,n,x,k,ans;
struct Mat{
int a[N][N];
friend Mat operator *(const Mat &X,const Mat &Y){
Mat ans;mem(ans.a,0);
for(int k=0;k<x;k++)for(int i=0;i<x;i++)for(int j=0;j<x;j++)ans.a[i][j]=(ans.a[i][j]+X.a[i][k]*Y.a[k][j])%MOD;
return ans;
}
}F,G;
int qpow(int x,int y){
int ans=1;
while(y){
if(y&1)ans=ans*x%MOD;
x=x*x%MOD,y>>=1;
}
return ans;
}
void Matqpow(int k){
while(k){
if(k&1)F=F*G;
G=G*G,k>>=1;
}
}
void solve(){
cin>>n>>x>>k,ans=qpow(2*k+1,n-1)*(x+k)%MOD;
for(int i=0;i<x;i++)F.a[0][i]=1;
for(int i=0;i<x;i++)for(int j=0;j<x;j++)G.a[i][j]=(abs(i-j)<=k);
Matqpow(n-1);
for(int i=0;i<x;i++)ans=(ans-F.a[0][i]+MOD)%MOD;
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
for(cin>>T;T--;)solve();
return 0;
}