[agc017F]Zigzag

来源:互联网 发布:ubuntu 字体 编辑:程序博客网 时间:2024/06/04 19:41

题目大意

有一个n行的三角形,第i行有i个格子。第i行第j个格子用(i,j)表示。从(i,j)可以到达(i+1,j)和(i+1,j+1)。现在要确定m条从(1,1)出发到第n行的路径。设第a条路径走到的第b个格子是(b,X[a,b]),对于任意a < b,不能存在i,使得X[a,i]>X[b,i]。同时还有K条形如(a,b,c)的限制,表示第a条路径第b个点到第b+1个点必须往方向c走。
求合法的方案数模109+7

n,m≤20

分析

如果尝试直接表示某一行m条路径的状态,运行效率是很低的。
考虑到每一个点出发只有两个方向,那么可以用二进制数来表示一条路径(0表示往(i+1,j)走,1表示(i+1,j+1))。当确定第i条路径的时候,只要知道第i-1条路径就可以了。
假设正在确定第i条路径,并且路径前j-1位已经确定了,同时前j-1位还和第i-1条路径相同。设S表示第i-1条路径的状态。现在如果S的第j位为0,且第i条路径的第j位为1,那么第j位出现开始不同。接下来我们找到S在第j位之后的第一个1,把这个1变成0,并把第j位赋为1,即强行把第i-1条路径改成前j位与第i条一致,容易证明这是等价的。因为在这两位之间无论第i条路径怎么走都不会不合法。
如果找不到下一个1,那么第i条路径之后就可以随便走了。
根据上面的思路就可以DP了。时间复杂度O(nm2n1)

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int N=20,M=524288,mo=1e9+7;typedef long long LL;int n,m,K,lim[N][N],g[M],f[2][M],p,q,nxt[N][M],h[N][M],ans,T;int main(){    scanf("%d%d%d",&n,&m,&K); T=1<<n-1;    memset(lim,255,sizeof(lim));    for (int a,b;K--;scanf("%d",&lim[a-1][b-1])) scanf("%d%d",&a,&b);    for (int i=1,j,k,la;i<M;i++)    {        for (j=T>>1,k=n-2,la=0;k>=0;k--,j>>=1)        {            nxt[k][i]=la;            if (j&i) la=j;        }    }    g[0]=1;    for (int i=0,j,k,st;i<m;i++)    {        p=0; q=1;        memcpy(f[0],g,sizeof(g));        memset(h,0,sizeof(h));        for (j=0,k=1;j<n-1;j++,p^=1,q^=1,k<<=1)        {            memset(f[q],0,sizeof(f[q]));            for (st=0;st<T;st++) if (f[p][st]>0)            {                if (lim[i][j]==-1)                {                    f[q][st]=(f[q][st]+f[p][st])%mo;                    if (!(k&st))                    {                        if (!nxt[j][st]) h[j+1][st|k]=(h[j+1][st|k]+f[p][st])%mo;                        else f[q][(st|k)^nxt[j][st]]=(f[q][(st|k)^nxt[j][st]]+f[p][st])%mo;                    }                }else if (lim[i][j]==0)                {                    if ((k&st)) continue;                    f[q][st]=(f[q][st]+f[p][st])%mo;                }else                {                    if ((k&st))                    {                        f[q][st]=(f[q][st]+f[p][st])%mo;                    }else                    {                        if (!nxt[j][st]) h[j+1][st|k]=(h[j+1][st|k]+f[p][st])%mo;                        else f[q][(st|k)^nxt[j][st]]=(f[q][(st|k)^nxt[j][st]]+f[p][st])%mo;                    }                }            }        }        for (j=2,k=2;j<n;j++,k<<=1)        {            for (st=0;st<k;st++) if (h[j-1][st]>0)            {                if (lim[i][j-1]!=1) h[j][st]=(h[j][st]+h[j-1][st])%mo;                if (lim[i][j-1]!=0) h[j][st|k]=(h[j][st|k]+h[j-1][st])%mo;            }        }        for (st=0;st<T;st++) g[st]=(f[p][st]+h[n-1][st])%mo;    }    ans=0;    for (int st=0;st<T;st++) ans=(ans+g[st])%mo;    printf("%d\n",ans);    return 0;}
原创粉丝点击