题目
称一个长为n的数列a是fancy的,当且仅当:
1. 数组内至少有一个元素在[x,x+k-1]之间
2. 相邻项的差的绝对值不超过k,即
t(t<=50)组样例,每次给定n(1<=n<=1e9),x(1<=x<=40),
求fancy的数组的数量,答案对1e9+7取模
思路来源
灵茶山艾府群 && 官方题解
题解
看到至少的字眼,首先想到容斥,用总的减不满足的,
本题中,合法方案数=[最小值<=x+k-1的方案数]-[最大值<x的方案数]
最小值<=x+k-1的方案数
第一个数字选0,后面每个数都有2k+1种选择方式,最后把最小值往上平移到[0,x+k-1]之间
方案数为
最小值<x的方案数
即长为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<bits/stdc++.h>
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<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int mod=1e9+7;
int t,n,x,k;
int modpow(int x,int n,int mod){int res=1;for(;n;n>>=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<mn;i++){c[i][i]=1;}}mat operator *(const mat& x){mat ans(m,x.n);for(int i=0;i<m;i++)for (int k=0;k<n;k++){if(!c[i][k])continue;//小剪枝for (int j=0;j<x.n;j++)(ans.c[i][j]+=c[i][k]*x.c[k][j]%mod)%=mod;//能不取模 尽量不取模}//这里maxn=2 故不会超过ll 视具体情况 改变内部取模情况return ans;}friend mat operator^(mat x,ll n){//幂次一般为ll 调用时a=a^(b,n) 等价于a=b^nmat ans(x.m, x.m);ans.E();for(;n;n>>=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=1while(t--){pte(sol());}return 0;
}