题目
Summer Training 06 - Amritapuri 2012 总结
题意:
在所有长度为n的01串中,不包含长度大于等于K的回文字串的串有多少个。
题解:
注意到K非常小,所以可以用状压。记录长为i和最后K个字符的串的个数,那么最后加个1或者0都不能使回文串长度大于等于K。即预处理所有的K种后缀里面,以最后一个字符为右端点的回文串的最长的和第二长的长度,即左端点的位置,那么加上1或者0之后就知道是否长度增加了。
//Time:350ms //Memory:8192KB //Length:2046B #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <set> using namespace std; #define MP(x,y) make_pair(x,y) #define MAXN 1000010 #define MAXM (1<<10) #define FI first #define SE second #define MOD 1000000007 set<pair<int,int> >se; int num[MAXN]; pair<int,int> ty[MAXM]; int dp[2][MAXM]; bool vi[MAXN]; pair<int,int> cal(int h,int k) { pair<int,int> pa=MP(0,0); for(int i=k-1;i>=0;--i) if(((h&(1<<i))>0)==((h&1)>0)) { int cnt=i?2:1; for(int l=i-1,r=1;l>=r;--l,++r) if(((h&(1<<l))>0)==((h&(1<<r))>0)) cnt+=(l!=r)?2:1; else { cnt=0; break; } if(cnt>pa.FI) pa.SE=pa.FI,pa.FI=cnt; else if(cnt>pa.SE) pa.SE=cnt; } return pa; } int main() { //freopen("/home/moor/Code/input","r",stdin); int ncase,n,k,ans=0,top; scanf("%d",&ncase); while(ncase--) { scanf("%d%d",&n,&k); if(n<k) { ans=1; while(n--) ans=ans*2%MOD; printf("%d\n",ans); continue; } int s=0; memset(dp[s],0,sizeof(dp[s])); top=(1<<k)-1; for(int i=0;i<=top;++i) { ty[i]=cal(i,k); if(ty[i].FI<k) dp[s][i]=1; } for(int i=k;i<n;++i) { memset(dp[!s],0,sizeof(dp[!s])); for(int j=0;j<=top;++j) for(int l=0;l<2;++l) { int tmp=((j<<1)&top)|l; if(ty[j].FI>=k) continue; if(l==((j&(1<<ty[j].FI))>0)&&2+ty[j].FI>=k) continue; if(l==((j&(1<<ty[j].SE))>0)&&2+ty[j].SE>=k) continue; dp[!s][tmp]=(dp[!s][tmp]+dp[s][j])%MOD; } s=!s; } ans=0; for(int i=0;i<=top;++i) ans=(ans+dp[s][i])%MOD; printf("%d\n",ans); } return 0; }