2012-A3-0010

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

题意是说,一个类似拨水管游戏的格子图,每一格有L形水管,一形水管,或者空,然后求从最左流到最右有几种方案,题目有一个重要限制:水不能往左流。

而且题目中的格子图,连续的若干列有重复性。

如果从左往右,可以经过一形直接通过

也可以经过一个L形再通过同一列连续的一形在经过一个L形向右。

从左往右只有以上两种方式。

剩下的就是利用连续的若干列有重复性

a[i][j],表示从第i行进,第j行出的方案数,如果只是一列就计算上述两种方式.

剩下的就是把每一列的矩阵a[i][j]乘起来即可,需要用快速幂算法加速..

{{{
#include<cstdio>
#include<cstring>
typedef unsigned num;
typedef unsigned long long ull;
const num mo = 11142457;
num a[6][6],b[6][6],tmp[6][6],ret[6][6],k;
ull t,n;
char s[8];
void mul(num z[][6],num x[][6],num y[][6]){
    memset(z,0,k*6<<2);
    for(num i=0;i<k;++i)
        for(num j=0;j<k;++j)
            for(num l=0;l<k;++l)
                z[i][j]= (z[i][j]+x[i][l]*(ull)y[l][j])%mo;
}
void mov(num dst[][6],num src[][6]){
    for(num i=0;i<k;++i)
        for(num j=0;j<k;++j)
            dst[i][j]=src[i][j];
}
void pow(ull x){
    if(x!=1llu){
        pow(x>>1);
        mul(tmp,b,b);
        if(x&1)mul(b,tmp,a);
        else mov(b,tmp);
    }else mov(b,a);
}
int main(){
    for(num m,ans;2==scanf("%llu%u",&n,&k);printf("%u\n",ans)){
        memset(ret,0,sizeof(ret));
        for(num i=0;i<k;++i)ret[i][i]=1;
        for(scanf("%u",&m);m--;){
            scanf("%llu",&t);
            for(num i=0;i<k;++i)scanf(" %c",s+i);
            memset(a,0,sizeof(a));
            for(num i=0;i<k;++i)
            if(s[i]!='0'){
                if(s[i]=='1'){
                    a[i][i]=1;continue;
                }else{
                    num j;
                    for(j=i-1;~j && s[j]=='1';--j);
                    if(~j&&s[j]=='2')a[i][j]=1;
                    for(j=i+1;j<k && s[j]=='1';++j);
                    if(j<k&&s[j]=='2')a[i][j]=1;
                }
            }
            pow(t);mul(tmp,ret,b);
            mov(ret,tmp);
        }
        for(num i=ans=0;i<k;++i)
            for(num j=0;j<k;++j)
                ans=(ans+ ret[i][j])%mo;
    }
}
}}}

题意是说,一个类似拨水管游戏的格子图,每一格有L形水管,一形水管,或者空,然后求从最左流到最右有几种方案,题目有一个重要限制:水不能往左流。

而且题目中的格子图,连续的若干列有重复性。

如果从左往右,可以经过一形直接通过

也可以经过一个L形再通过同一列连续的一形在经过一个L形向右。

从左往右只有以上两种方式。

剩下的就是利用连续的若干列有重复性

a[i][j],表示从第i行进,第j行出的方案数,如果只是一列就计算上述两种方式.

剩下的就是把每一列的矩阵a[i][j]乘起来即可,需要用快速幂算法加速..

#include<cstdio>
#include<cstring>
typedef unsigned num;
typedef unsigned long long ull;
const num mo = 11142457;
num a[6][6],b[6][6],tmp[6][6],ret[6][6],k;
ull t,n;
char s[8];
void mul(num z[][6],num x[][6],num y[][6]){
    memset(z,0,k*6<<2);
    for(num i=0;i<k;++i)
        for(num j=0;j<k;++j)
            for(num l=0;l<k;++l)
                z[i][j]= (z[i][j]+x[i][l]*(ull)y[l][j])%mo;
}
void mov(num dst[][6],num src[][6]){
    for(num i=0;i<k;++i)
        for(num j=0;j<k;++j)
            dst[i][j]=src[i][j];
}
void pow(ull x){
    if(x!=1llu){
        pow(x>>1);
        mul(tmp,b,b);
        if(x&1)mul(b,tmp,a);
        else mov(b,tmp);
    }else mov(b,a);
}
int main(){
    for(num m,ans;2==scanf("%llu%u",&n,&k);printf("%u\n",ans)){
        memset(ret,0,sizeof(ret));
        for(num i=0;i<k;++i)ret[i][i]=1;
        for(scanf("%u",&m);m--;){
            scanf("%llu",&t);
            for(num i=0;i<k;++i)scanf(" %c",s+i);
            memset(a,0,sizeof(a));
            for(num i=0;i<k;++i)
            if(s[i]!='0'){
                if(s[i]=='1'){
                    a[i][i]=1;continue;
                }else{
                    num j;
                    for(j=i-1;~j && s[j]=='1';--j);
                    if(~j&&s[j]=='2')a[i][j]=1;
                    for(j=i+1;j<k && s[j]=='1';++j);
                    if(j<k&&s[j]=='2')a[i][j]=1;
                }
            }
            pow(t);mul(tmp,ret,b);
            mov(ret,tmp);
        }
        for(num i=ans=0;i<k;++i)
            for(num j=0;j<k;++j)
                ans=(ans+ ret[i][j])%mo;
    }
}