【题解】方块染色(容斥原理+巧妙分类)
刚开始以为是道容斥,写了这个错误程序
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\n",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\n",dp[n]);
}
return 0;
}
原文地址:https://www.cnblogs.com/winlere/p/11415448.html
时间: 2024-10-09 09:22:40