ARC158F 题解

First Post:

Last Update:

感觉转化限制的那一步比较难,和题解想的不大一样,怎么都难以优化。

首先观察一个操作序列,显然,只有每个数位最后一次出现的位置是有用的。假设操作序列中一共出现了 种数位,那么可以设一个大小为 的排列 表示数位 最后一次出现的位置排在第几名(也就是这个关键字第几个被考虑到)。

显然,一个排列能被多少个操作序列生成只与排列大小 有关,因为每一位的地位是相同的。进一步地,方案数就是 。分子是第二类斯特林数,表示将每次操作分配给一个数位,每种数位都必须出现。这个斯特林数包含了所有大小为 的排列,单个排列的方案除以阶乘即可。

接下来考虑什么样的排列 能生成序列 我们只需让 排在 的前面,而不需要尝试保证任何 都在 前面。

考虑一对 ,对于每个关键字,存在一些位 满足 ,令这些位组成的集合为 。存在一些位 满足 ,令这些位组成的集合为 。那么对于任意 ,都必须存在一个 使得 先作为依据进行排序。另外,如果 中的出现位置是逆序,那么整个过程中也必须以 中的某个 为依据进行排序。(代码中将 出现的位置也当作一个关键字处理了)

那么,设 表示 集合中是否存在一个必须比 先作为依据排序的关键字。在处理一对 时,先将 赋值为 。再对 做一遍高维前缀或即可处理出 数组。

那么生成原来的排列 也很简单了。按照高优先级到低优先级的顺序进行 DP。设 表示当前已经以 中的关键字为依据排序过的可行排列数。转移时枚举 ,使得 为假,转移到 即可。

时间复杂度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5,Sz = (1 << 18) | 5,P = 998244353,MxK = 20;
inline void Plus(int &a,const int &b) { a += b;if(a >= P) a -= P;}
inline int qpow(int a,int b) { int res = 1;while(b) {if(b&1) res = 1ll * res * a % P;a = 1ll * a * a % P;b >>= 1;} return res;}
typedef long long ll;
int n,m,K;
ll a[N],b[N];
bool h[MxK][Sz];
int dp[Sz];

inline int C(int n,int m) {
if(n < 0 || m < 0 || n < m) return 0;
int res = 1;
for(int i = n - m + 1;i <= n;i++) res = 1ll * res * i % P;
for(int i = 1;i <= m;i++) res = 1ll * res * qpow(i,P - 2) % P;
return res;
}
inline int Strling(int n,int m) {
if(n < 0 || m <= 0 || n < m) return 0;
int res = 0;
for(int i = 0;i <= m;i++)
if(i & 1) Plus(res,P - 1ll * C(m,i) * qpow(m - i,n) % P);
else Plus(res,1ll * C(m,i) * qpow(m - i,n) % P);
return res;
}
ll Pow10[MxK];
inline int Get(ll x,int v) { return (x / Pow10[v]) % 10;} // v 从 0 开始为最低位
int f[N],g[N];
int keys[N][MxK];
map<ll,vector<int> > aps;

int main() {
cin >> n >> m >> K;
for(int i = 1;i <= n;i++) cin >> a[i],aps[a[i]].push_back(i);
for(int i = 1;i <= n;i++) cin >> b[i];
Pow10[0] = 1;
for(int i = 1;i <= K;i++) Pow10[i] = 10ll * Pow10[i - 1];
for(int i = n;i >= 1;i--) {
for(int j = 0;j < K;j++)
keys[i][j] = Get(b[i],j);
keys[i][K] = aps[b[i]].back();
aps[b[i]].pop_back();
}
for(int i = 1;i <= K;i++) {
f[i] = Strling(m,i);
for(int j = 1;j <= i;j++)
f[i] = 1ll * f[i] * qpow(j,P - 2) % P;
}
for(int i = 1;i < n;i++) {
int S = 0,T = 0;
for(int j = 0;j <= K;j++) {
if(keys[i][j] < keys[i + 1][j] && j < K) S |= (1 << j);
if(keys[i][j] > keys[i + 1][j]) T |= (1 << j);
}
for(int j = 0;j <= K;j++)
if(T >> j & 1)
h[j][S] = 1;
}
auto PreSum = [&](int id) {
for(int k = 1;k < (1 << K);k <<= 1)
for(int j = 0;j < (1 << K);j += k + k)
for(int t = 0;t < k;t++)
h[id][j | t | k] |= h[id][j | t];
};
for(int i = 0;i <= K;i++) PreSum(i);
dp[0] = 1;
for(int S = 0;S < (1 << K);++S) {
for(int i = 0;i <= K;i++)
if(!h[i][(1 << K) - 1 - S] && (~S >> i & 1)) {
if(i == K) Plus(g[__builtin_popcount(S)],dp[S]);
else Plus(dp[S | (1 << i)],dp[S]);
}
}
int ans = 0;
for(int i = 0;i <= K;i++)
Plus(ans,1ll * f[i] * g[i] % P);
cout << ans << endl;
return 0;
}