team2012-E1-mysol-0010

从 Trac 迁移的文章

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

原文章内容如下:

{{{
/* 解题报告 //

给出一个 k 行 n 列的网格,每个格子有三种类型:墙、直通道、L 型通道
列一共有 m 组,在每组中,同一行的格子类型是相同的
你可以任意设定管道的旋转方向,并在左边缘上任选一个格子作为入口
问有多少种方案可以到达右边缘

显然,可以用 dp[i][j] 表示到达 i 行 j 列那个格子左边缘时的方案数
若 a[i][j] = 0,dp[i][j + 1] = 0
若 a[i][j] = 1,dp[i][j + 1] = dp[i][j]
若 a[i][j] = 2,dp[i][j + 1] = dp[x][j] + dp[y][j]
  其中 x < i,y > i,a[x][j] = a[y][j] = 2
  且 a[x + 1 .. i - 1] = a[i + 1 .. y - 1] = 1
  即第 i 行,第 x 行和第 y 行的那三个格子都为 L 型通道
  在 x 行和 y 行之间的其他格子都为直通道

写出上面的 dp 式可以发现,这种线性的式子可以用矩阵进行加速计算
于是把转移方程写到矩阵里然后去做就可以了

// By 猛犸也钻地 @ 2012.07.20 */
}}}

{{{
#include <cstdio>
#include <vector>
using namespace std;

class MatInt{
public:
    static const int MOD = 11142457;
    typedef long long LL;
    typedef vector<int> BAR;
    int n,m;
    vector<BAR> u;
    MatInt():n(),m(){}
    MatInt(int c):u(c,BAR(c)){for(n=m=c;c--;u[c][c]=1);}
    MatInt(int n, int m):n(n),m(m),u(n,BAR(m)){}
    BAR& operator [](int idx){return u[idx];}
    const BAR& operator [](int idx) const{return u[idx];}
    operator bool() const{return n && m;}
    MatInt& operator *=(const MatInt& c){return *this=*this*c;}
    MatInt operator ~() const{
        MatInt ret(m,n);
        for(int i=0;i<m;i++) for(int j=0;j<n;j++) ret[i][j]=u[j][i];
        return ret;
    }
    MatInt operator *(const MatInt& c) const{
        if(m!=c.n) return MatInt();
        MatInt ret(n,c.m),v=~c;
        for(int i=0;i<n;i++) for(int j=0;j<c.m;j++) for(int k=0;k<m;k++)
            ret[i][j]=(ret[i][j]+LL(u[i][k])*v[j][k])%MOD;
        return ret;
    }
    MatInt pow(LL e) const{
        if(n!=m) return MatInt();
        MatInt ret=n,c=*this;
        for(;e;e>>=1,c*=c) if(e&1) ret*=c;
        return ret;
    }
};

int main(){
    int n,m,c[10];
    long long x;
    while(scanf("%lld%d",&x,&m)==2){
        MatInt a(1,m);
        for(int i=0;i<m;i++) a[0][i]=1;
        scanf("%d",&n);
        while(n--){
            scanf("%lld",&x);
            for(int i=0;i<m;i++) scanf("%d",c+i);
            MatInt e(m,m);
            for(int i=0;i<m;i++){
                if(c[i]==1) e[i][i]=1;
                if(c[i]!=2) continue;
                int j=i,k=i;
                while(--j>=0 && c[j]==1);
                while(++k< m && c[k]==1);
                if(j>=0) e[i][j]=1;
                if(k <m) e[i][k]=1;
            }
            a*=e.pow(x);
        }
        int ans=0;
        for(int i=0;i<m;i++) ans=(ans+a[0][i])%a.MOD;
        printf("%d\n",ans);
    }
}
}}}
/* 解题报告 //
给出一个 k 行 n 列的网格,每个格子有三种类型:墙、直通道、L 型通道
列一共有 m 组,在每组中,同一行的格子类型是相同的
你可以任意设定管道的旋转方向,并在左边缘上任选一个格子作为入口
问有多少种方案可以到达右边缘
显然,可以用 dp[i][j] 表示到达 i 行 j 列那个格子左边缘时的方案数
若 a[i][j] = 0,dp[i][j + 1] = 0
若 a[i][j] = 1,dp[i][j + 1] = dp[i][j]
若 a[i][j] = 2,dp[i][j + 1] = dp[x][j] + dp[y][j]
  其中 x < i,y > i,a[x][j] = a[y][j] = 2
  且 a[x + 1 .. i - 1] = a[i + 1 .. y - 1] = 1
  即第 i 行,第 x 行和第 y 行的那三个格子都为 L 型通道
  在 x 行和 y 行之间的其他格子都为直通道
写出上面的 dp 式可以发现,这种线性的式子可以用矩阵进行加速计算
于是把转移方程写到矩阵里然后去做就可以了
// By 猛犸也钻地 @ 2012.07.20 */
#include <cstdio>
#include <vector>
using namespace std;
class MatInt{
public:
    static const int MOD = 11142457;
    typedef long long LL;
    typedef vector<int> BAR;
    int n,m;
    vector<BAR> u;
    MatInt():n(),m(){}
    MatInt(int c):u(c,BAR(c)){for(n=m=c;c--;u[c][c]=1);}
    MatInt(int n, int m):n(n),m(m),u(n,BAR(m)){}
    BAR& operator [](int idx){return u[idx];}
    const BAR& operator [](int idx) const{return u[idx];}
    operator bool() const{return n && m;}
    MatInt& operator *=(const MatInt& c){return *this=*this*c;}
    MatInt operator ~() const{
        MatInt ret(m,n);
        for(int i=0;i<m;i++) for(int j=0;j<n;j++) ret[i][j]=u[j][i];
        return ret;
    }
    MatInt operator *(const MatInt& c) const{
        if(m!=c.n) return MatInt();
        MatInt ret(n,c.m),v=~c;
        for(int i=0;i<n;i++) for(int j=0;j<c.m;j++) for(int k=0;k<m;k++)
            ret[i][j]=(ret[i][j]+LL(u[i][k])*v[j][k])%MOD;
        return ret;
    }
    MatInt pow(LL e) const{
        if(n!=m) return MatInt();
        MatInt ret=n,c=*this;
        for(;e;e>>=1,c*=c) if(e&1) ret*=c;
        return ret;
    }
};
int main(){
    int n,m,c[10];
    long long x;
    while(scanf("%lld%d",&x,&m)==2){
        MatInt a(1,m);
        for(int i=0;i<m;i++) a[0][i]=1;
        scanf("%d",&n);
        while(n--){
            scanf("%lld",&x);
            for(int i=0;i<m;i++) scanf("%d",c+i);
            MatInt e(m,m);
            for(int i=0;i<m;i++){
                if(c[i]==1) e[i][i]=1;
                if(c[i]!=2) continue;
                int j=i,k=i;
                while(--j>=0 && c[j]==1);
                while(++k< m && c[k]==1);
                if(j>=0) e[i][j]=1;
                if(k <m) e[i][k]=1;
            }
            a*=e.pow(x);
        }
        int ans=0;
        for(int i=0;i<m;i++) ans=(ans+a[0][i])%a.MOD;
        printf("%d\n",ans);
    }
}