题解 P5343 【【XR-1】分块】
比赛时忘记去重没AC。。。
60分:根据题意分析,先把两人要求的分块大小公共部分处理出来。 对于当前长度,枚举它可以是由哪些长度转移过来即可。
比如说样例1,公共部分是1,2,序列长度为4。长度为4的序列可以由长度为2的序列再填一个长度为2的块,或者长度为3的序列填一个长度为1的块组成。如果设长度为i的方案数为f[i],那么f[4]=f[2]+f[3]。显然是个递推,边界f[0]=1(长度为0只能啥块都不用),对于每个长度i,枚举块长j,f[i]+=f[i-j]。复杂度O(nx);
100分:发现上边的式子是一个线性齐次递推式(放弃解释概念),说白了就是跟斐波那契数列有点像,所以可以矩阵快速幂。
复杂度O(x^3logn)。
最后奉上代码。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
long long n;
int pr,nf,tot=0;
int a[150],b[150];
int solve[150],vis[150];
long long f[150];
struct node{
long long sq[105][105];
}power,ans;
//a[i]=sum(a[i]-solve[j])
node add(node x,node y){
node c;
memset(c.sq,0,sizeof(c.sq));
for(int i=1;i<=solve[tot];i++){
for(int j=1;j<=solve[tot];j++){
for(int k=1;k<=solve[tot];k++){
c.sq[i][j]=(c.sq[i][j]+(x.sq[i][k]*y.sq[k][j])%mod)%mod;
}
}
}
return c;
}
int main(){
memset(f,0,sizeof(f));
scanf("%lld",&n);
scanf("%d",&pr);
for(int i=1;i<=pr;i++){
scanf("%d",&a[i]);
}
scanf("%d",&nf);
for(int i=1;i<=nf;i++){
scanf("%d",&b[i]);
}
sort(a+1,a+pr+1);
sort(b+1,b+nf+1);
int cnta=1,cntb=1;
memset(vis,0,sizeof(vis));
while(cnta<=pr&&cntb<=nf){
if(a[cnta]==b[cntb]){
if(!vis[a[cnta]])solve[++tot]=a[cnta];
vis[a[cnta]]=1;
cnta++;
cntb++;
}
else if(a[cnta]<b[cntb]){
cnta++;
}
else cntb++;
}
f[0]=1;
for(int i=1;i<=solve[tot];i++){
for(int j=1;j<=tot;j++){
if(i-solve[j]>=0)f[i]=(f[i]+f[i-solve[j]])%mod;
}
}
memset(ans.sq,0,sizeof(ans.sq));
for(int i=1;i<=solve[tot];i++){
ans.sq[i][i]=1;
}
memset(power.sq,0,sizeof(power.sq));
for(int i=2;i<=solve[tot];i++){
power.sq[i][i-1]=1;
}
for(int i=1;i<=tot;i++){
power.sq[solve[tot]+1-solve[i]][solve[tot]]=1;
}
long long p=n-solve[tot];
while(p){
if(p&1){
ans=add(ans,power);
}
power=add(power,power);
p>>=1;
}
long long cnt=0;
for(int i=1;i<=solve[tot];i++){
cnt=(cnt+f[i]*(ans.sq[i][solve[tot]])%mod)%mod;
}
printf("%lld\n",cnt);
return 0;
}
常数写的有点大,最大的一个点跑了900+ms。。。