Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

CF785E Anton and Permutation

CF785E Anton and Permutation

CF: E. Anton and Permutation

Algorithm I 线段树套平衡树

比较裸,大常数两只log,过不了。

查看代码
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
81
82
#include <cstdio>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
template <class Type>
void read (Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 1) + (x << 3) + c - '0';
flag && (x = ~x + 1);
}
template <class Type, class ...rest>
void read (Type &x, rest &...y) { read(x), read(y...); }
template <class Type>
void write (Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar('0' + x % 10);
}
typedef long long LL;
typedef tree <int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> Tree;
const int N = 2e5 + 10;
LL ans;
int n, m, w[N];
struct Node { int l, r; Tree t; } tr[N << 2];
void build (int l = 1, int r = n, int x = 1)
{
tr[x].l = l, tr[x].r = r;
for (int i = l; i <= r; ++i) tr[x].t.insert(w[i]);
if (l == r) return;
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
}
void add (int t, int x = 1)
{
tr[x].t.insert(w[t]);
if (tr[x].l == tr[x].r) return;
int mid = tr[x].l + tr[x].r >> 1;
add(t, x << 1 | (t > mid));
}
void del (int t, int x = 1)
{
tr[x].t.erase(w[t]);
if (tr[x].l == tr[x].r) return;
int mid = tr[x].l + tr[x].r >> 1;
del(t, x << 1 | (t > mid));
}
int QueryLess (int l, int r, int k, int x = 1)
{
if (l > r || tr[x].l > r || l > tr[x].r) return 0;
if (tr[x].l >= l && tr[x].r <= r) return tr[x].t.order_of_key(k);
return QueryLess(l, r, k, x << 1) + QueryLess(l, r, k, x << 1 | 1);
}
int QueryGreater (int l, int r, int k, int x = 1)
{
if (l > r || tr[x].l > r || l > tr[x].r) return 0;
if (tr[x].l >= l && tr[x].r <= r) return tr[x].t.size() - tr[x].t.order_of_key(k + 1);
return QueryGreater(l, r, k, x << 1) + QueryGreater(l, r, k, x << 1 | 1);
}
int main ()
{
read(n, m);
for (int i = 1; i <= n; ++i) w[i] = i;
build();
for (int a, b; m; --m)
{
read(a, b);
if (a > b) swap(a, b);
ans -= QueryLess(a + 1, b - 1, w[a]) + QueryGreater(a + 1, b - 1, w[b]) + (w[a] > w[b]);
ans += QueryLess(a + 1, b - 1, w[b]) + QueryGreater(a + 1, b - 1, w[a]) + (w[b] > w[a]);
del(a), del(b), swap(w[a], w[b]), add(a), add(b);
write(ans), puts("");
}
return 0;
}

Algorithm II 分块套树状数组

在 OI-Wiki 上被称为这个名称,并被分入“树套树”标题下,实际上问题就是二维偏序,如果直接用树状数组那么空间爆炸。分块后空间由 $O(n ^ 2)$ 减少为 $O(n ^ {\frac 3 2})$ ,可以接受。复杂度 单次复杂度 $O(\sqrt n + log ^ 2)$ ,常数较小。

查看代码
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
81
82
#include <cstdio>
#include <algorithm>
using namespace std;
template <class Type>
void read (Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
c == '-' && (flag = true);
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 1) + (x << 3) + c - '0';
flag && (x = ~x + 1);
}
template <class Type, class ...rest>
void read (Type &x, rest &...y) { read(x), read(y...); }
template <class Type>
void write (Type x)
{
x < 0 && (putchar('-'), x = ~x + 1);
x > 9 && (write(x / 10), 0);
putchar('0' + x % 10);
}
typedef long long LL;
const int N = 2e5 + 10, B = 450;
LL ans;
int n, m, w[N], tot, id[N], L[N], R[N], tr[B][N];
void add (int x, int y, int k)
{
for (int i = x; i <= tot; i += i & -i)
for (int j = y; j <= n; j += j & -j)
tr[i][j] += k;
}
int query (int x, int y)
{
int res = 0;
for (int i = x; i; i -= i & -i)
for (int j = y; j; j -= j & -j)
res += tr[i][j];
return res;
}
int QueryLess (int l, int r, int k) { return query(r, k - 1) - query(l - 1, k - 1); }
int QueryGreater (int l, int r, int k) { return (r - l + 1) - QueryLess(l, r, k + 1); }
int main ()
{
read(n, m);
for (int i = 1; i <= n; ++i)
id[i] = (i - 1) / B + 1;
tot = id[n];
for (int i = 1; i <= tot; ++i)
L[i] = R[i - 1] + 1, R[i] = R[i - 1] + B;
R[tot] = n;
for (int i = 1; i <= n; ++i) add(id[i], w[i] = i, 1);
for (int a, b; m; --m)
{
read(a, b);
if (a > b) swap(a, b);
if (id[a] == id[b])
{
ans += (w[b] > w[a]) - (w[a] > w[b]);
for (int i = a + 1; i < b; ++i)
ans += (w[b] > w[i]) + (w[i] > w[a]) - (w[a] > w[i]) - (w[i] > w[b]);
swap(w[a], w[b]);
}
else
{
ans += (w[b] > w[a]) - (w[a] > w[b]);
for (int i = a + 1; i <= R[id[a]]; ++i)
ans += (w[b] > w[i]) + (w[i] > w[a]) - (w[a] > w[i]) - (w[i] > w[b]);
ans -= QueryGreater(id[a] + 1, id[b] - 1, w[b]) + QueryLess(id[a] + 1, id[b] - 1, w[a]);
ans += QueryGreater(id[a] + 1, id[b] - 1, w[a]) + QueryLess(id[a] + 1, id[b] - 1, w[b]);
for (int i = L[id[b]]; i < b; ++i)
ans += (w[b] > w[i]) + (w[i] > w[a]) - (w[a] > w[i]) - (w[i] > w[b]);
add(id[a], w[a], -1), add(id[b], w[b], -1);
swap(w[a], w[b]);
add(id[a], w[a], 1), add(id[b], w[b], 1);
}
write(ans), puts("");
}
return 0;
}