CF1895F Fancy Arrays 题解

CF1895F Fancy Arrays

题意

定义一个长度为 n n n 的非负整数序列是 Fancy 的,当且仅当:

  1. ∃ 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+k1]
  2. ∀ i ∈ [ 2 , n ] \forall i\in [2, n] i[2,n] ∣ a i − a i − 1 ∣ ≤ k |a_i - a_{i - 1}| \leq k aiai1k

1 ≤ n , k ≤ 1 0 9 1\leq n,k\leq 10^9 1n,k109 0 ≤ x ≤ 40 0\leq x\leq 40 0x40

多测,给定 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+k1]

我们先看 “并且至少有一个数属于 [ x , x + k − 1 ] [x,x+k-1] [x,x+k1]” 这一条件,需要满足这一条件,就需要同时满足 max ⁡ i = 1 n a i ≥ x \max_{i=1}^na_i\ge x maxi=1naix min ⁡ i = 1 n a i ≤ x + k − 1 \min_{i=1}^{n}a_i\le x+k-1 mini=1naix+k1

容斥原理转化一下,我们就可以,用 min ⁡ i = 1 n a i ≤ x + k − 1 \min_{i=1}^{n}a_i\le x+k-1 mini=1naix+k1 的方案总数减去 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=1naix+k1的方案总数如何求

我们发现只要确定了 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+k1 这个区间中所以有 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 cik),所以有 2 k + 1 2k+1 2k+1 种选择,差分数组上有 n − 1 n-1 n1 个位置,所以有 ( 2 k + 1 ) n − 1 (2k+1)^{n-1} (2k+1)n1 种选择。根据简单的配列组合知识,就可以得到第一部分的总方案数是 ( x + k ) × ( 2 k + 1 ) n − 1 (x+k)\times (2k+1)^{n-1} (x+k)×(2k+1)n1

(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=jkj+kfi1,t,但是题目 n n n 的数据范围太大并且 x x x 的范围很小,又发现 f f f 的转移式恒定不变,考虑矩阵快速幂优化,具体方法如下:

定义初始一个 1 ∗ x 1*x 1x的矩阵 F \text{F} F 以及一个 x ∗ x x*x xx 转移矩阵 G \text{G} G,其中 F \text{F} F的初始值都是 1 1 1 G \text{G} G 的初始值若 ∣ i − j ∣ ≤ k |i-j|\le k ijk 则令 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;
}
  • 26
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值