zoukankan      html  css  js  c++  java
  • CodeForces621E 快速矩阵幂优化dp

    有时些候在用快速矩阵幂优化dp的时候,它的矩阵乘法是不那么容易被具体为题目背景的意思的,大多数时候难以理解矩阵之间相乘的实际意义,正如有时候我们不知道现在在做手头这些事情的意义,但倘若是因一个目标而去做的,正如快速矩阵幂最终会计算出答案一样,我们也最终会在这些不明意义的事情中实现目标。

    题意:有 bb 个格子,每个格子有 nn 个数字,各个格子里面的数字都是相同的. 求从 bb 个格子中各取一个数字, 构成一个 bb 位数, 使得这个 bb 位数模 xx 为 kk 的方案数(同一格子内相同的数字算不同方案)

     

    由于每个格子的数都是0-9的,我们首先可以想到用num存所有数字的数量。

    一个简单的思想是dp每一位数字的余数,dp[i][j]表示遍历到i的时候有余数j的可能性数量。

    写出状态转移方程 dp[i][j * 10 + k] += dp[i - 1][j] * num[k] 

    但是i的数量大到1e9,显然是不可能的,事实上我们可以考虑用快速矩阵幂来优化,

    用一个大小为x * x的矩阵来表示从一个余数到另一个余数的可能情况直接上快速矩阵幂即可。

    #include <map>
    #include <set>
    #include <ctime>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <vector>
    #include <string>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <sstream>
    #include <iostream>
    #include <algorithm>
    #include <functional>
    using namespace std;
    #define For(i, x, y) for(int i=x;i<=y;i++)  
    #define _For(i, x, y) for(int i=x;i>=y;i--)
    #define Mem(f, x) memset(f,x,sizeof(f))  
    #define Sca(x) scanf("%d", &x)
    #define Sca2(x,y) scanf("%d%d",&x,&y)
    #define Scl(x) scanf("%lld",&x);  
    #define Pri(x) printf("%d
    ", x)
    #define Prl(x) printf("%lld
    ",x);  
    #define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
    #define LL long long
    #define ULL unsigned long long  
    #define mp make_pair
    #define PII pair<int,int>
    #define PIL pair<int,long long>
    #define PLL pair<long long,long long>
    #define pb push_back
    #define fi first
    #define se second 
    typedef vector<int> VI;
    const double eps = 1e-9;
    const int maxn = 110;
    const int maxm = 5e4 + 10;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7; 
    int N,B,K,X;
    int a[maxm];
    int num[14];
    struct Mat{
        LL a[maxn][maxn];
        void init(){
            Mem(a,0);
        }
    };
    Mat operator *(Mat a,Mat b){
        Mat ans; ans.init();
        for(int i = 0 ; i < X; i ++){
            for(int j = 0 ; j < X; j ++){
                for(int k = 0 ; k < X; k ++){
                    ans.a[i][j] = (ans.a[i][j] + a.a[i][k] * b.a[k][j]) % mod;
                }
            }
        }
        return ans;
    }
    int main()
    {
        scanf("%d%d%d%d",&N,&B,&K,&X);
        For(i, 1, N){
            scanf("%d", &a[i]);
            a[i] %= X;
            num[a[i]]++;
        }
        Mat base,ans; base.init(); ans.init();
        ans.a[0][0] = 1;
        for(int i = 0 ; i < X; i ++){
            for(int j = 0 ; j < 10 ; j ++){
                int to = (i * 10 + j) % X;
                base.a[i][to] += num[j];
            }
        }
        while(B){
            if(B & 1) ans = ans * base;
            base = base * base;
            B >>= 1;
        }
        Prl(ans.a[0][K]);
        #ifdef VSCode
        system("pause");
        #endif
        return 0;
    }
  • 相关阅读:
    summary
    谷歌浏览器Software Reporter Tool长时间占用CPU解决办法
    进栈 出栈 标准用法
    C语言保证,0永远不是有效的数据地址,因此,返回址0可用来表示发生的异常事件
    寄存器是内存阶层中的最顶端,也是系统获得操作资料的最快速途径。 存于寄存器内的地址可用来指向内存的某个位置,即寻址
    对内存分配的理解 自动变量 局部变量 临时变量 外部变量 字符串长度 C语言可以看成由一些列的外部对象构成
    ORM Active Record Data Mapper
    summary
    c预处理器
    #include<stdio.h> #include "stdio.h"
  • 原文地址:https://www.cnblogs.com/Hugh-Locke/p/9631622.html
Copyright © 2011-2022 走看看