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

本文介绍了一种使用矩阵快速幂方法解决特定连通块转移问题的方法。通过判断初始状态的特性,利用矩阵运算高效计算经过多次转移后的连通块数量,适用于竞赛编程中的复杂问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

很神的题!!!

首先观察一下每次转移

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

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

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

x 等于黑块的数量, y 等于初始图像中 ai,jai,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、付费专栏及课程。

余额充值