【题解】方块染色(容斥原理+巧妙分类)
刚开始以为是道容斥,写了这个错误程序
int main(){
pre(1e5);
while(~scanf("%d%d",&n,&m)){
int ans=0;
for(register int t=m,delta;t<=n;++t){
delta=0;
for(register int k=1,d2;k<=n/t;++k){
d2=1ll*c(n/t,k)*(n-k*t+1)%mod*bin[n-k*t]%mod;
if(k&1) d2=mod-d2;
delta+=d2;
while(delta>=mod)delta-=mod;
}
if((t-m)&1) delta=mod-delta;
ans=(ans+delta)%mod;
}
printf("%d
",ans);
}
return 0;
}
这显然是错的,容斥套容斥,我不知道为什么我敢交上去(可能是对了样例)
后来我通过仔(bai)细(du)思(ti)考(jie)获得了启示
设(dp(n))为(n)个时的方案数
假如我已经知道前面(n-1)的方案,现在要知道(n)的方案了,怎么做?
显然地,肯定有一个(2dp(n-1))的贡献,意思是在后面加上一个块,什么颜色无所谓
然后我通过思(ti)考(jie)发现,可以这样分类:
-
加上去的没有作用,加不加这一块对于满足合法条件没有贡献,直接加就好了,什么颜色无所谓,所以就是(2dp(n-1))
-
加上去的有作用,那就是说,整个序列只有后缀的(m)个元素有用,其他都是打酱油,那么先钦定后面(m)个都是红色,现在问题就是前面(n-m)个要求不能产生作用,这样才能满足我们这里的分类。
那么就是总数-不合法呗,但是且慢,假如说我前面(n-m)个和后面这个(m)个连接在一起了,就会和加上去没有作用的那个分类重复。所以我要钦定倒数第(m+1)是蓝色,所以前面总共有(2^{n-m-1}-dp(n-m-1))有贡献了。
你可能会问,那可能存在一种合法方案,使得是倒数(m+x)个使得整个序列满足条件啊,这种方案去哪了? 这种方案被放在了第一个分类中,因为这样的话,最后一个块什么颜色就无所谓了,少了不会使得整个序列不满足条件。
所以直接递推:
[dp(x)=2dp(x-1)+2^{x-m-1}-dp(x-m-1)
]
初始条件(dp(m)=1)
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; typedef long long ll;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=1e5+5;
const int mod=1e9+7;
int bin[maxn],dp[maxn];
int n,m;
int main(){
for(register int t=bin[0]=1;t<=100000;++t){
bin[t]=bin[t-1]<<1;
while(bin[t]>=mod) bin[t]-=mod;
}
while(~scanf("%d%d",&n,&m)){
dp[m]=1;
for(register int t=m+1;t<=n;++t){
dp[t]=dp[t-1]<<1;
while(dp[t]>mod) dp[t]-=mod;
dp[t]+=bin[t-m-1];
while(dp[t]>mod) dp[t]-=mod;
if(t-m-1>=m) dp[t]+=mod-dp[t-m-1];
while(dp[t]>mod) dp[t]-=mod;
}
printf("%d
",dp[n]);
}
return 0;
}