[Medium] AT_abc390_g [ABC390G] Permutation Concatenation
2025/3/20约 776 字大约 4 分钟
简要题意
对于一个排列 ,定义 表示排列的每一个数按顺序依次拼接得到的十进制数。
你需要计算对于所有长度为 的排列 的 之和。答案对 取模。
。
思路
约定几个符号:
- 也就是 在十进制下的数位数。
- 。
先拆贡献,记 为 的贡献,则有:
即枚举 为倒数第 个数,然后钦定后面 个数的选法(那么前面的选法也就随之确定了,最后注意要乘上的不是方案数而是移位贡献),最后乘上阶乘表示顺序。最后答案就是 。
考察:
于是可以 求出 。
然后我们考虑原式:
注意到自变量其实可以改为 ,于是定义 :
于是我们只需要高效求出 即可。
注意到 ,直接卷积即可。时间复杂度 。
代码
#include <bits/stdc++.h>
using namespace std;
/* hide poly template */
using poly::Poly;
constexpr int mod = 998244353;
int Add(int x, int y){ return (x + y) >= mod ? (x + y - mod) : (x + y); }
int Sub(int x, int y){ return (x - y) < 0 ? (x - y + mod) : (x - y); }
int Mul(int x, int y){ return 1ll * x * y % mod; }
int fastpow(int x, int y){
int ret = 1;
for(;y;y>>=1,x=Mul(x, x)){ if(y & 1) ret = Mul(ret, x); }
return ret;
}
const int N = 2e5 + 5;
int n, m, cnt[N], fact[N], cnts[N];
int lambda(int x){ return floor(log10(x)) + 1; }
signed main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for(int i=1;i<=n;i++) cnt[lambda(i)]++, cnts[lambda(i)] = Add(cnts[lambda(i)], i);
fact[0] = 1, m = lambda(n);
for(int i=1;i<=n;i++) fact[i] = Mul(fact[i - 1], i);
Poly f; f.resize(n + 1);
for(int j=1;j<=n;j++){
int tmp = 0;
for(int i=1;i<=m;i++) tmp = Add(tmp, Mul(fastpow(10, i * j), cnt[i]));
f[j] = Mul(Mul((j & 1 ? 1 : mod - 1), fastpow(j, mod - 2)), tmp);
}
f = f.exp();
int ans = 0;
for(int i=1;i<=m;i++){
Poly g; g.resize(n + 1);
int tmp = mod - fastpow(10, i);
for(int j=0;j<=n;j++) g[j] = fastpow(tmp, j);
g = f * g;
int ret = 0;
for(int j=0;j<n;j++) ret = Add(ret, Mul(Mul(fact[j], fact[n - j - 1]), g[j]));
ans = Add(ans, Mul(ret, cnts[i]));
}
cout << ans << '\n';
return 0;
}
// Written by xiezheyuan