P3157

[CQOI2011]动态逆序对

一开始想了个 naive 的树套树。

显然是可做的。

但实际上用 CDQ 分治更为巧妙。

我们把被删除时间另外作一维,这个问题就又能化成三维偏序。

统计的目标为删除该数之后将会减少多少逆序对。

实际处理的时候要左对右,再右对左进行两种贡献统计。

另外还需要单独统计答案。

时间复杂度 $O(n\log n)$。

代码:

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;

const ll N=1e5;

ll n,m,ans;

ll delta[N+5],c[N+5];

inline void add(ll x,ll y) {
for(;x<=m+1;x+=x&-x) c[x]+=y;
}

inline ll ask(ll x) {
ll res=0;for(;x;x-=x&-x) res+=c[x];return res;
}

struct node{
ll a,b,c;
bool operator < (const node& rhs) const{
return a>rhs.a;
}
}a[N+5],tmp[N+5];

void solve(ll l,ll r) {
if(l==r) return;
ll mid=(l+r)>>1;
solve(l,mid);solve(mid+1,r);
ll i=l,j=mid+1,k=l;
while(j<=r) {
while(i<=mid) {
if(a[i].b<a[j].b) {
add(a[i].c,1);
tmp[k++]=a[i++];
}
else break;
}
ans+=i-l;
delta[a[j].c]+=ask(m+1)-ask(a[j].c-1);
tmp[k++]=a[j++];
}
while(i<=mid) {
add(a[i].c,1);tmp[k++]=a[i++];
}
while(j<=r) {
ans+=i-l;
delta[a[j].c]+=ask(m+1)-ask(a[j].c-1);
tmp[k++]=a[j++];
}
for(ll i=l;i<=mid;i++) add(a[i].c,-1);
i=mid;j=r;
while(i>=l) {
while(j>=mid+1) {
if(a[j].b>a[i].b) {
add(a[j].c,1);j--;
}
else break;
}
delta[a[i].c]+=ask(m+1)-ask(a[i].c-1);
i--;
}
while(j>=mid+1) {
add(a[j].c,1);j--;
}
while(i>=l) {
delta[a[i].c]+=ask(m+1)-ask(a[i].c-1);
i--;
}
for(ll i=mid+1;i<=r;i++) add(a[i].c,-1);
for(ll i=l;i<=r;i++) a[i]=tmp[i];
}

inline ll read() {
ll ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-f;ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
return ret*f;
}

inline void write(ll x) {
static char buf[22];static ll len=-1;
if(x>=0) {
do{buf[++len]=x%10+48;x/=10;}while(x);
}
else {
do{buf[++len]=-(x%10)+48;x/=10;}while(x);
}
while(len>=0) putchar(buf[len--]);
}

inline void writeln(ll x) {
write(x);putchar('\n');
}

int main() {

n=read();m=read();

for(ll i=1;i<=n;i++) {
ll x;x=read();
a[x].b=i;a[x].a=x;
}

for(ll i=1;i<=m;i++) {
ll x;x=read();
a[x].c=i;
}

for(ll i=1;i<=n;i++) {
if(!a[i].c) a[i].c=m+1;
}

sort(a+1,a+n+1);

solve(1,n);

for(ll i=1;i<=m;i++) {
writeln(ans);
ans-=delta[i];
}

return 0;
}