LA 3704 Cellular Automaton【循环矩阵,快速幂】

题目链接:

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1705

题意:

给定 n(1n500) 个格子,圆排列,规定每次操作将每个格子的值变为到他的距离不超过 d 的所有格子在操作之前的值之和模m(1m106),求 k(1k107) 次操作之后各个格子的值。

分析:

我们发现得到的每次每个元素得到的新值都可以看做是原来值的线性组合,因此可以将各个格子的值表示为列向量,用矩阵乘法来解决。
最初头脑一热写了个矩阵快速幂,发现结构体中的矩阵都开不下= = ,而且时间复杂度 O(n3logk) 也过不了。
仔细观察我们构造的矩阵可以发现从第二行开始每一行都是上一行的循环右移一位的结果,也就是构成了循环矩阵。。
可以发现两个循环矩阵的乘积也是循环矩阵,因此存储时只要保存一行即可,计算时稍加处理也可仅用一行表示,时间复杂度降到 O(n2logk) ,数组也开的下了= =

代码:


/*************************************************************************
    > File Name: LA3704.cpp
    > Author: jiangyuzhu
    > Mail: 834138558@qq.com 
    > Created Time: 2016/7/27 14:56:56
 ************************************************************************/

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<queue>
#include<cstring>
#include<stack>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 500 + 5;
int mod;
ll a[maxn];
int n, d;
ll k;
const int N = 500 + 5;
struct Matrix
{
    int sz;
    ll m[N];
};
Matrix init(Matrix a, ll t)
{
    for(int i = 0; i < a.sz; i++)
       a.m[i] = t;
    return a;
}
Matrix operator * (const Matrix& a, const Matrix& bb)
{
    Matrix b;b.sz = n;
    b = init(b, 0);
    for(int i = 0; i < n; i++){
        b.m[i] = bb.m[(n - i) % n];
    }
    Matrix ans;ans.sz = n;
    ans = init(ans,0);
    for(int j = 0; j < n; j++)
        for(int k = 0; k < n; k++)
            ans.m[j] = (ans.m[j] + a.m[k] * b.m[((k - j + n)) % n])%mod;
    return ans;
}
Matrix mul(Matrix a, Matrix b)
{
    Matrix ans;
    ans.sz = n;
    ans = init(ans,0);
    for(int j = 0; j < n; j++)
        for(int k = 0; k < n; k++)
            ans.m[j] = (ans.m[j] + a.m[(k - j + n) % n] * b.m[k])%mod;
    return ans;
}
Matrix quick_pow(ll k, Matrix A)
{
    Matrix I;I.sz = n;
    I = init(I, 0);
    for(int i = 0; i < n; i++){
        I.m[i] = a[i];
    }
    for(; k; k >>= 1, A = A * A){
        if(k & 1) I = mul(A, I);
    }
    return I;
}
int main (void)
{
    while(~scanf("%d%d%d%lld", &n, &mod, &d, &k)){
        for(int i = 0; i < n; i++){
            scanf("%lld", &a[i]);
            a[i] %= mod;
        }
        Matrix A;A.sz = n;A = init(A, 0);
        for(int i = 0; i < n; i++){
            if(min(n - i, i) <= d) A.m[i] = 1;
        }
        A = quick_pow(k, A);
        for(int i = 0; i < n; i++){
            printf("%lld%c", A.m[i], i == n - 1?'\n':' ');
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值