Educational Codeforces Round 157 (Rated for Div. 2) F. Fancy Arrays(容斥+组合数学)

题目

称一个长为n的数列a是fancy的,当且仅当:

1. 数组内至少有一个元素在[x,x+k-1]之间

2. 相邻项的差的绝对值不超过k,即|a_{i}-a_{i+1}|\leq k

t(t<=50)组样例,每次给定n(1<=n<=1e9),x(1<=x<=40),

求fancy的数组的数量,答案对1e9+7取模

思路来源

灵茶山艾府群 && 官方题解

题解

看到至少的字眼,首先想到容斥,用总的减不满足的,

本题中,合法方案数=[最小值<=x+k-1的方案数]-[最大值

最小值<=x+k-1的方案数

第一个数字选0,后面每个数都有2k+1种选择方式,最后把最小值往上平移到[0,x+k-1]之间

方案数为(x+k)*(2*k+1)^{n-1}

最小值

即长为n的数列,使用的值均在[0,x-1]的方案数,

注意到x<=40,所以可以dp[i][j]表示长为i的数组最后一个是j的方案数

转移时,只要abs(j1-j2)<=k,就可以从j1转移到j2,

构造上述转移矩阵,矩阵快速幂求出其n-1次幂,

由于长度为1时对应的[0,x-1]的向量均为1,所以将每一行的和从答案中减掉即可

代码

// Problem: F. Fancy Arrays
// Contest: Codeforces - Educational Codeforces Round 157 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1895/problem/F
// Memory Limit: 512 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<>=1,x=1ll*x*x%mod){
		if(n&1)res=1ll*res*x%mod;
	}
	return res;
}
struct mat{
    static const int N=42;
    ll c[N][N];
    int m,n;
    mat(){
    	memset(c,0,sizeof(c));
    	m=n=N;
    }
    mat(int a,int b):m(a),n(b){
        memset(c,0,sizeof(c));
    }
    void clear(){
		memset(c,0,sizeof(c));
    }
    void E(){
        int mn=min(m,n);
        for(int i=0;i>=1,x=x*x){//x自乘部分可以预处理倍增,也可以用分块加速递推
            if(n&1)ans=ans*x;
        }
        return ans;
	}
};
int sol(){
	sci(n),sci(x),sci(k);
	int ans=1ll*(x+k)*modpow(2*k+1,n-1,mod)%mod;
	if(!x)return ans;
	mat a(x,x);
	rep(i,0,x-1){
		rep(j,0,x-1){
			if(abs(i-j)<=k)a.c[i][j]=1;
		}
	}
	a=a^(n-1);
	rep(i,0,x-1){
		int sum=0;
		rep(j,0,x-1){
			sum=(sum+a.c[i][j])%mod;
		}
		ans=(ans-sum+mod)%mod;
	}
	return ans;
}
int main(){
	sci(t); // t=1
	while(t--){
		pte(sol());
	}
	return 0;
}

你可能感兴趣的:(组合数学(容斥原理),#,容斥,组合计数,矩阵快速幂)