[agc017f]Zigzag

来源:互联网 发布:淘宝评论在哪里看 编辑:程序博客网 时间:2024/06/16 17:45

前言

DP题,我当然不会啦。

题目大意

请你找到m个n位二进制数,对于相邻两个,i+1的前j位的和不小于i的前j位的和。
此外还有对于某个二进制数某位必须是几的若干个限制。

DP

很容易想到设dp[i,s]表示做到第i个二进制数第i个是s,每次枚举前一个,复杂度很大。
转移复杂度太大了,我们来尝试优化。
如果可以不用枚举前一个就好了。
那么不如让前一个对着当前的改变吧!
设dp[i,j,s]表示做到第i个二进制数,已经确定了它的前j位和s的前j位一致,其中你只要保证了这个新的二进制数任意前缀和和s满足,就能满足限制。
这个s就是前一个变化而来的。
变化规则也很简单。
如果你确定当前位为1,s这位也是1,当然没问题。
0与0同理。
而0与1非法。
然后1与0,则为了满足s的前j位与当前二进制数前j位一致,要对s做改变。
你可以找到在这之后第一个有1的位置,让s这一位-1,第j位+1。
当然之后可能已经没有1了,这说明当前二进制数可以为所欲为,然后我们直接把第j位+1即可。
这样dp就简单了。

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;const int maxn=20+10,mo=1000000007;int dp[2][maxn][(1<<21)+10],nxt[(1<<21)+10][maxn];int bz[maxn][maxn];int i,j,k,l,r,s,t,n,m,p,ans,now;int main(){    scanf("%d%d%d",&n,&m,&p);    n--;    fo(i,0,(1<<n)-1){        t=-1;        fd(j,n-1,0){            if ((i&(1<<j))) t=j;            nxt[i][j]=t;        }    }    fo(i,1,m)        fo(j,0,n-1)            bz[i][j]=-1;    fo(i,1,p){        scanf("%d%d%d",&j,&k,&l);        bz[j][k-1]=l;    }    dp[0][n][0]=1;    now=1;    fo(i,1,m){        fo(s,0,(1<<n)-1) dp[now][0][s]=dp[1-now][n][s];        fo(j,0,n)            fo(s,0,(1<<n)-1)                dp[1-now][j][s]=0;        fo(j,0,n-1)            fo(s,0,(1<<n)-1)                if (dp[now][j][s]){                    fo(k,0,1){                        if (bz[i][j]!=-1&&bz[i][j]!=k) continue;                        t=(s&(1<<j));                        if (t) t=1;                        if (k==0&&t==1) continue;                        if (k==t) (dp[now][j+1][s]+=dp[now][j][s])%=mo;                        else{                            r=nxt[s][j];                            if (r==-1) (dp[now][j+1][s+(1<<j)]+=dp[now][j][s])%=mo;                            else (dp[now][j+1][s+(1<<j)-(1<<r)]+=dp[now][j][s])%=mo;                        }                    }                }        now=1-now;    }    fo(s,0,(1<<n)-1) (ans+=dp[1-now][n][s])%=mo;    (ans+=mo)%=mo;    printf("%d\n",ans);}
原创粉丝点击