ARC160F 题解

First Post:

Last Update:

这道题加深了我对排列转化为 01 序列这个技巧的理解。

首先考虑怎么解决单次询问。排列的大小关系不好考虑,但如果我们考虑每个值 ,将 的值标为 的值标为 ,那么一个排列将对应一个 个 01 序列构成的组。或者说,对应了在 维空间中,一条从 走到 的路径。那么一个合法的排列,其对应的路径中的每个 01 序列必须是能被当前拥有的操作排好序的,因此,我们只需对于每个 01 序列算出其经过当前拥有的操作后是否排好序,然后进行状压 DP 即可。这一部分复杂度为单次

但因为有 询问,这个复杂度难以承受,考虑减小询问次数。

定义 “有用操作”为,存在一个排列,在进行了这个操作前的所有操作后,仍然能被这个操作改变状态。

考虑一个有用操作 和其对应的一个 的排列 ,以及一个位置 满足 。在进行一次 后, 这个操作本身会变得无用,但仍然可以使一些操作从无用变为有用,或从有用变为无用。假设操作完之后 从无用变得有用了,那么这说明一开始有 ,交换之后, ,此时 又从有用变得无用了。此时,我们发现无用操作数量增大了 。对于 的情况,也是类似的,所以我们可以说明,进行一次有用操作后,无用操作数量会增大至少

也就是说,整个过程中,其实只有 个操作是有效的。我们处理数组 表示当前操作 是否有效。在进行了一次有效操作后,我们发现只有 形式的操作的有效性会受影响,此时我们枚举所有 01 序列,暴力更新这些操作的可行性即可。这单次也是 的。

所以总复杂度为

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
#include <bits/stdc++.h>
using namespace std;
const int N = 20,Sz = 1 << 15;
typedef long long ll;
int n,m;
bool eff[N][N];
int bec[Sz]; // 一个 01 序列会被交换成什么样子
bool sorted[Sz];
ll dp[Sz];

int Get(int S,int x) { return S >> x & 1;}
inline ll DP() {
memset(dp,0,sizeof dp);
int all = (1 << n) - 1;
dp[all] = 1; // 在 i = 1 时序列全是 1,满足条件
for(int S = all - 1;S >= 0;S--) {
if(!sorted[bec[S]]) continue;
for(int j = 0;j < n;j++)
if(!Get(S,j))
dp[S] += dp[S | (1 << j)];
}
return dp[0];
}
int main() {
cin >> n >> m;
for(int i = 0;i < n;i++)
for(int j = i + 1;j < n;j++)
eff[i][j] = 1;
for(int i = 0;i <= n;i++)
sorted[((1 << i) - 1) << (n - i)] = true;
for(int i = 0;i < (1 << n);i++)
bec[i] = i;
ll lastans = 1;
for(int _ = 1;_ <= m;_++) {
int a,b,c,d;
cin >> c >> d;
c = (c + lastans) % n;
d = (d + lastans + lastans) % n;
a = min(c,d);b = max(c,d);
if(eff[a][b]) {
for(int i = 0;i < a;i++) eff[i][a] = 0;
for(int i = a + 1;i < n;i++) eff[a][i] = 0;
for(int i = 0;i < b;i++) eff[i][b] = 0;
for(int i = b + 1;i < n;i++) eff[b][i] = 0;
for(int i = 0;i < (1 << n);i++) {
if(Get(bec[i],a) > Get(bec[i],b))
bec[i] ^= (1 << a) ^ (1 << b);
for(int j = 0;j < a;j++) eff[j][a] |= Get(bec[i],j) > Get(bec[i],a);
for(int j = a + 1;j < n;j++) eff[a][j] |= Get(bec[i],a) > Get(bec[i],j);
for(int j = 0;j < b;j++) eff[j][b] |= Get(bec[i],j) > Get(bec[i],b);
for(int j = b + 1;j < n;j++) eff[b][j] |= Get(bec[i],b) > Get(bec[i],j);
}
lastans = DP();
printf("%lld\n",lastans);
} else printf("%lld\n",lastans);
}
return 0;
}