CF621E Wet Shark and Blocks
考虑记录哪些信息是必要的:当前是第几位,以及当前余数是多少。注意,当前填什么完全不重要,我们只在意填之前和填之后的余数。
记\(f_{k, i}\) 表示当前填了 \(k\) 位,余数为 \(i\),多少种方案。
有转移 \(f_{k, i} = \sum_{j = 0} ^ {x - 1}f_{k - 1, j} \times a_{i, j}\),其中 \(a_{i, j}\) 表示余数从 \(j\) 变成 \(i\) 的贡献。
利用滚动数组的思想可以优化掉第一维,然后就可以利用矩阵加速了。构造一个 \(x \times x\) 的矩阵 \(a\) 和一个长度为 \(x\) 的列向量 \(b\),二者都可以通过暴力枚举来初始化。
答案就是 \(a ^ {x - 1} \times b\) 的第 \(k\) 行。时间复杂度 \(O(x^3 \times \log_{2}^{b})\)。
注意矩阵快速幂的基本形式是 \(a^{x - 1} \times b\) 而不是 \(b \times a^{x - 1}\),注意矩阵乘法的运算法则。
然后就是写代码之前一定要把 \(a\) 的行、列的含义想清楚。
#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l); i <= (r); ++ i)
#define G(i,r,l) for(int i(r); i >= (l); -- i)
using namespace std;
using ll = long long;
int n, len, k, x;
ll num[200], cnt[200], f[200];
ll ans = 0;
const int mod = 1e9 + 7;
struct matrix{
ll a[200][200];
matrix(int val){
memset(a, 0, sizeof(a));
F(i, 1, x){
a[i][i] = val;
}
}
matrix operator *(matrix &other){
matrix c(0);
F(i, 1, x){
F(j, 1, x){
F(k, 1, x){
c.a[i][k] = (c.a[i][k] + a[i][j] * other.a[j][k] % mod) % mod;
}
}
}
return c;
}
};
matrix operator ^(matrix x, int y){
matrix c(1);
while(y){
if(y & 1) c = c * x;
x = x * x;
y >>= 1;
}
return c;
}
void Main(){
cin >> n >> len >> k >> x;
F(i, 1, n){
int val;
cin >> val;
++ cnt[val];
}
matrix b(0);
F(i, 0, x - 1){ // las
F(j, 1, 9){
b.a[(10 * i + j) % x + 1][i + 1] += cnt[j];
}
}
F(i, 1, 9){
f[i % x] += cnt[i];
}
b = b ^ (len - 1);
ll sm = 0;
F(i, 1, x){
sm = (sm + b.a[k + 1][i] * f[i - 1] % mod) % mod;
}
cout << sm << '\n';
return ;
}
signed main(){
// freopen("wet.in", "r", stdin);
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T = 1;
while(T --) Main();
return fflush(0), 0;
}

浙公网安备 33010602011771号