[矩阵快速幂] Atcoder AGC003 F. Fraction of Fractal

很神的题!!!

首先观察一下每次转移

如果初始的图案存在某一行 i ,满足行的两个端点为黑,且存在某一列也是满足这样的条件,那么答案就是1,因为原来就连通的黑块转移后的图案也是联通的。

如果行和列都不满足,因为每次转移答案都会乘 x, 那么答案就是 xk1 ,其中 x 为初始图案中的黑块数量。

如果只有行满足这样的条件(列满足的话转一下图案),

x 等于黑块的数量, y 等于初始图像中 ai,j ai,j+1 都为黑的 (i,j) 数量, z 为几行满足上面的条件

那么每次转移就有

  • x=x2

    • y=xy+yz
    • z=z2
    • 这个东西退一下可以知道…

      用矩阵转移就好了…

      #include <cstdio>
      #include <iostream>
      #include <algorithm>
      #include <cstring>
      #include <assert.h>
      
      using namespace std;
      
      typedef long long ll;
      
      const int N=1010,P=1e9+7;
      
      struct mat{
        int a[2][2];
        mat(){ a[0][0]=a[1][1]=a[0][1]=a[1][0]=0; }
        int *operator [](int x){
          return a[x];
        }
        friend mat operator *(mat a,mat b){
          mat c;
          c[0][0]=(1LL*a[0][0]*b[0][0]+1LL*a[0][1]*b[1][0])%P;
          c[0][1]=(1LL*a[0][0]*b[0][1]+1LL*a[0][1]*b[1][1])%P;
          c[1][0]=(1LL*a[1][0]*b[0][0]+1LL*a[1][1]*b[1][0])%P;
          c[1][1]=(1LL*a[1][0]*b[0][1]+1LL*a[1][1]*b[1][1])%P;
          return c;
        }
      };
      
      mat u;
      int n,m;
      char a[N][N],b[N][N];
      ll k;
      
      inline int judge(){
        for(int i=1;i<=n;i++)
          if(a[i][1]=='#' && a[i][m]=='#') return 1;
        return 0;
      }
      
      inline void turn(){
        for(int i=1;i<=n;i++)
          for(int j=1;j<=m;j++)
            b[m-j+1][i]=a[i][j];
        swap(n,m); memcpy(a,b,sizeof(a));
      }
      
      inline int Pow(int x,ll y){
        int ret=1;
        for(;y;y>>=1,x=1LL*x*x%P) if(y&1) ret=1LL*ret*x%P;
        return ret;
      }
      
      inline mat Pow(mat x,ll y){
        mat ret=u;
        for(;y;y>>=1,x=x*x) if(y&1) ret=ret*x;
        return ret;
      }
      
      int main(){
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
        u[0][0]=u[1][1]=1;
        scanf("%d%d%lld",&n,&m,&k);
        int cnt=0;
        for(int i=1;i<=n;i++){
          scanf("%s",a[i]+1);
          for(int j=1;j<=m;j++)
            cnt+=a[i][j]=='#';
        }
        int A=judge(),B=(turn(),judge()); turn();
        if((A && B) || !k) return puts("1"),0;
        if(!A && !B) return printf("%d\n",Pow(cnt,k-1)),0;
        if(!A) turn();
        int c=0,b=0; for(int i=1;i<=n;i++) c+=(a[i][1]=='#' && a[i][m]=='#');
        for(int i=1;i<=n;i++)
          for(int j=1;j<m;j++)
            b+=(a[i][j]=='#' && a[i][j+1]=='#');
        mat w,ans; w[0][0]=cnt; w[0][1]=b; w[1][1]=c;
        ans=Pow(w,k-1);
        printf("%d\n",(ans[0][0]+P-ans[0][1])%P);
        return 0;
      }
      
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值