UVA - 1386 Cellular Automaton (矩阵快速幂)

Time Limit: 18000MS  64bit IO Format: %lld & %llu

 Status uDebug

Description

Download as PDF

题意:

对一个圈里的n个数字进行操作,每次操作都将数字i与其相邻距离d以内的数字加起来对m取余。共进行k次操作,问你k次操作后各数为多少。

分析:

感觉上是矩阵快速幂,没想到n这么大,书上写的循环矩阵悟了半天也悟不到,最后貌似由于矩阵开太大,传参时爆内存。最后发现了循环矩阵的做法是只求第一行,但是处理矩阵相乘时需要注意,因为循环矩阵可由第一行求出整个矩阵,因此顺序需要搞清楚。相乘搞定了其他都是基本的快速幂过程。

#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3f
const long long N=505;
const long long mod=1e9;
const double PI=acos(-1.0);
typedef long long ll;

typedef struct {
    ll mat[N];
}Mat;

int n,d,m,k;

Mat multi(Mat a,Mat b) {
    Mat c;
    memset(c.mat, 0, sizeof(c.mat));
    for (int i=1; i<=n; i++) {
        for (int j=1; j<=n; j++) {
            c.mat[i]+=a.mat[j]*b.mat[(i-j+n)%n+1];
            c.mat[i]%=m;
        }
    }
    return c;
}

Mat qui(Mat a,int b) {
    Mat c;
    memset(c.mat, 0, sizeof(c.mat));
    
    
    c.mat[1]=1;
    while (b) {
        if (b&1) {
            c=multi(c, a);
        }
        a=multi(a, a);
        b>>=1;
    }
    return c;
}

int main() {
    int f[N];
    Mat A;
    while (cin>>n>>m>>d>>k) {
        memset(f, 0, sizeof(f));
        for (int i=1; i<=n; i++) {
            scanf("%d",&f[i]);
        }
        memset(A.mat, 0, sizeof(A.mat));
        
        for (int i=1; i<=1; i++) {
            A.mat[i]=1;
            for (int j=1; j<=d; j++) {
                A.mat[(i-1+j)%n+1]=1;
                A.mat[(i-1-j+n)%n+1]=1;
            }
        }
        A=qui(A, k);
        ll res[N];
        memset(res, 0, sizeof(res));
        for (int i=1; i<=n; i++) {
            for (int j=1; j<=n; j++) {
                //                cout<<A.mat[(j+i-2)%n+1]*f[j]<<endl;
                res[i]+=A.mat[(i-j+n)%n+1]*f[j];
                res[i]%=m;
            }
        }
        for (int i=1; i<n; i++) {
            printf("%lld ",res[i]);
        }
        cout<<res[n]<<endl;
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值