CodeForces621E 快速矩阵幂优化dp

时间:2021-09-12 15:08:34

有时些候在用快速矩阵幂优化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\n", x)
#define Prl(x) printf("%lld\n",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-;
const int maxn = ;
const int maxm = 5e4 + ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,B,K,X;
int a[maxm];
int num[];
struct Mat{
LL a[maxn][maxn];
void init(){
Mem(a,);
}
};
Mat operator *(Mat a,Mat b){
Mat ans; ans.init();
for(int i = ; i < X; i ++){
for(int j = ; j < X; j ++){
for(int k = ; 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, , N){
scanf("%d", &a[i]);
a[i] %= X;
num[a[i]]++;
}
Mat base,ans; base.init(); ans.init();
ans.a[][] = ;
for(int i = ; i < X; i ++){
for(int j = ; j < ; j ++){
int to = (i * + j) % X;
base.a[i][to] += num[j];
}
}
while(B){
if(B & ) ans = ans * base;
base = base * base;
B >>= ;
}
Prl(ans.a[][K]);
#ifdef VSCode
system("pause");
#endif
return ;
}