【ZOJ】Nice Patterns Strike Back(矩阵快速乘法)

dp[[i][j] = sum(dp[i - 1][k]) (k -> j)

状态方程,因为N很大而M很小,所以第一时间可以想到矩阵优化

可能之前没做过类似的题被卡的很厉害。

另外用C++写大数真心麻烦。。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 45;
const int maxd = 30005;
char n[maxd],n_two[maxd],s[maxd];
int  m,mod,cntx;
struct Matx{
    int n,m;
    int mat[maxn][maxn];
    Matx(){
        memset(mat,0,sizeof(mat));
    }
    friend Matx operator * (Matx p,Matx q){
        Matx temp;
        memset(temp.mat,0,sizeof(temp.mat));
        temp.n = p.n; temp.m = q.m;
        for(int i = 0; i < p.n; i++){
            for(int j = 0; j < q.m; j++){
                for(int k = 0; k < q.n; k++){
                    temp.mat[i][j] += p.mat[i][k] * q.mat[k][j];
                    temp.mat[i][j] %= mod;
                }
            }
        }
        return temp;
    }
};
Matx base,ans,start;
void cost(){
    int L = strlen(n);
    n[L - 1] --;
    for(int i = L - 1; i >= 0; i--){
        int e = n[i] - '0';
        if(e >= 0) break;
        else{
            n[i - 1] --;
            n[i] += 10;
        }
    }
    int t;
    for(t = 0; t < L && n[t] == '0'; t++);
    if(t == L) n[L++] = '0';
    n[L] = '\0';
    strcpy(s,n + t);
    strcpy(n,s);
}
void init(){
    int t = (1 << m);
    base.n = base.m = t;
    memset(ans.mat,0,sizeof(ans.mat));
    memset(start.mat,0,sizeof(start.mat));
    for(int i = 0; i < base.n; i++){
        for(int j = 0; j < base.m; j++){
            base.mat[i][j] = 1;
            for(int k = 0; k < m - 1; k++){
                int a1 = (1 << k) & i,a2 = (1 << k) & j;
                int b1 = (1 << (k + 1)) & i,b2 = (1 << (k + 1)) & j;
                if(!a1 && !b1 && !a2 && !b2){
                    base.mat[i][j] = 0;
                    break;
                }
                else if(a1 && b1 && a2 && b2){
                    base.mat[i][j] = 0;
                    break;
                }
            }
        }
    }
    ans.n = 1; ans.m = t;
    for(int i = 0; i < ans.m; i++)
        ans.mat[0][i] = 1;
    start.n = start.m = t;
    for(int i = 0; i < start.n; i++)
        start.mat[i][i] = 1;
}
bool can_div2(){
    int L = strlen(n);
    int v = 0;
    for(int i = 0; i < L; i++){
        v = v * 10 + n[i] - '0';
        v %= 2;
    }
    if(!v) return true;
    return false;
}
void solve_div2(){
    int L = strlen(n);
    int v = 0,cnt = 0;
    for(int i = 0; i < L; i++){
        v = v * 10 + n[i] - '0';
        int e = v / 2;
        s[cnt++] = e + '0';
        v %= 2;
    }
    int t;
    for(t = 0; t < cnt && s[t] == '0'; t++);
    if(cnt == t) s[cnt++] = '0';
    s[cnt] = '\0';
    strcpy(n,s + t);
}
void solve_to_two(){
    if(strcmp(n,"0") == 0) return;
    if(can_div2()) n_two[cntx++] = '0';
    else n_two[cntx++] = '1';
    solve_div2();
    solve_to_two();
}
Matx Pow(){
    int cc = 0;
    for(int i = 0; i < cntx; i++){
        if(n_two[i] == '1'){
            start = start * base;
            cc ++;
        }
        base = base * base;
    }
    ans = ans * start;
    int ret = 0;
    for(int i = 0; i < ans.m; i++){
        ret += ans.mat[0][i];
        ret %= mod;
    }
    printf("%d\n",ret);
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        cntx = 0;
        scanf("%s%d%d",n,&m,&mod);
        cost();
        init();
        solve_to_two();
        Pow();
        if(T)
            puts("");
    }
    return 0;
}
时间: 2024-10-03 00:38:46

【ZOJ】Nice Patterns Strike Back(矩阵快速乘法)的相关文章

ZOJ 3690 &amp; HDU 3658 (矩阵快速幂+公式递推)

ZOJ 3690 题意: 有n个人和m个数和一个k,现在每个人可以选择一个数,如果相邻的两个人选择相同的数,那么这个数要大于k 求选择方案数. 思路: 打表推了很久的公式都没推出来什么可行解,好不容易有了想法结果WA到天荒地老也无法AC.. 于是学习了下正规的做法,恍然大悟. 这道题应该用递推 + 矩阵快速幂. 我们设F(n) = 有n个人,第n个人选择的数大于k的方案数: G(n) = 有n个人,第n个人选择的数小于等于k的方案数: 那么递推关系式即是: F(1)=m?k,G(1)=k F(n

ZOJ 2105 Number Sequence(矩阵快速幂)

题意: f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7. 给定A,B,求f(n). 法一: 网上较多的题解都提到了寻找1 1循环节的方法,的确非常巧妙,每位0~6,共7种可能,相邻两位共49种可能,因此循环周期至多为49,一旦出现相同数对,那么其后必相同.但是,该方法只是简单提及了49,却并没有证明1 1循环节一定存在,没有排除可能前面一段不循环,后面一段开始周期性循环的可能性.(是我悟性太差吗,为什么大多数题解都只谈

zoj 2317 Nice Patterns Strike Back(矩阵乘法)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1317 给出一个n*m的矩阵(n <= 10^100, m <= 5),对于2*2的子方格若全是黑色或全是白色的是非法的,用黑白两色去染n*m的方格,问共有多少种合法的染色方案. 构造出转移矩阵,上一行向下一行的转移矩阵,因为m<=5,每行最多有32个状态,可以进行状态压缩构造出一个32*32的转移矩阵A,A[i][j] = 1表示上一行i状态可以向下一行的j状态转移,

ZOJ 3690 Choosing number(矩阵快速幂)

题目地址:ZOJ 3690 假设F(n)表示前n个人第n个人选择的数大于k的个数,G(n)表示的是前n个人第n个人选择的数小于等于k的个数 那么F(n) = F(n-1)*(m-k)+G(n-1)*(m-k) , G(n) = F(n-1)*k+G(n-1)*(k-1) , 那么最后的结果就是F(n)+G(n); 那么我们可以构造出矩阵 | m-k m-k|   | F(n-1) |       | F(n) | | k      k-1| * | G(n-1) | => | G(n) | 那么

ZOJ 2853 Evolution 【简单矩阵快速幂】

这道题目第二次看的时候才彻底理解了是什么意思 把题目转化为数学模型分析后就是 有一个初始序列, 有一个进化率矩阵 求的是初始序列 与进化率矩阵进行 m 次运算后, 初始序列最后一位的答案 那么显然,可以对进化率矩阵进行快速幂计算 Example Let's assume that P(0, 1) = P(1, 2) = 1, and at the beginning of a sub-process, the populations of 0, 1, 2 are 40, 20 and 10 re

快速乘法/快速幂 算法

快速幂算法可以说是ACM一类竞赛中必不可少,并且也是非常基础的一类算法,鉴于我一直学的比较零散,所以今天用这个帖子总结一下 快速乘法通常有两类应用:一.整数的运算,计算(a*b) mod c  二.矩阵快速乘法 一.整数运算:(快速乘法.快速幂) 先说明一下基本的数学常识: (a*b) mod c == ( (a mod c) * (b mod c) ) mod c //这最后一个mod c 是为了保证结果不超过c 对于2进制,2n可用1后接n个0来表示.对于8进制,可用公式 i+3*j ==

ZOJ2317-Nice Patterns Strike Back:矩阵快速幂,高精度

Nice Patterns Strike Back Time Limit: 20000/10000MS (Java/Others)Memory Limit: 128000/64000KB (Java/Others) Problem Description You might have noticed that there is the new fashion among rich people to have their yards tiled with black and white tile

zoj 2974 Just Pour the Water矩阵快速幂

Just Pour the Water Time Limit: 2 Seconds      Memory Limit: 65536 KB Shirly is a very clever girl. Now she has two containers (A and B), each with some water. Every minute, she pours half of the water in A into B, and simultaneous pours half of the

zoj 2853 Evolution(矩阵快速幂)

Evolution is a long, long process with extreme complexity and involves many species. Dr. C. P. Lottery is currently investigating a simplified model of evolution: consider that we haveN (2 <= N <= 200) species in the whole process of evolution, inde