1. 程式人生 > 實用技巧 >【CQOI2011】動態逆序對

【CQOI2011】動態逆序對

分析

近乎裸的 \(cdq\) 分治數點問題
我們考慮一個數被刪去,它對刪後區間逆序對個數的影響就是減去現存序列中前面比它大的個數再減去現存序列中後面比它小的個數
那麼我們考慮如何處理時間限制
既然是“現存序列中”,也就是說刪去時間比它晚的
那麼能產生貢獻的數對 \((i,j)\) 就要滿足 \(i < j,val_i > val_j,time_i > time_j\)
這是前面的貢獻
而後面的貢獻同理,所以我們在每輪分治中算左區間對右區間的貢獻以及右區間對左區間的貢獻
注意:刪後還沒刪完的若干個數我們讓它們的刪去時間依次遞增
因為沒刪完的數可以相互貢獻,但我們不能重複算同一個數對
所以我們讓時間遞增,然後取嚴格大於來算貢獻

\(Code\)

#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;

const int N = 1e5 + 5;
int n , m , rd[N] , ans[N] , c[N] , up;

struct node{
	int a , b , id;
}f[N];

inline bool cmp(node x , node y){return x.a > y.a;}
inline int lowbit(int x){return x & (-x);}
inline void add(int x , int v){for(; x <= up; x += lowbit(x)) c[x] += v;}
inline int query(int x)
{
	int res = 0;
	for(; x; x -= lowbit(x)) res += c[x];
	return res;
}

inline void solve(int l , int r)
{
	if (l == r) return;
	int mid = (l + r) >> 1;
	solve(l , mid) , solve(mid + 1 , r);
	sort(f + l , f + mid + 1 , cmp) , sort(f + mid + 1 , f + r + 1 , cmp);
	int j = l;
	for(register int i = mid + 1; i <= r; i++)
	{
		while (f[j].a > f[i].a && j <= mid) add(f[j].b , 1) , j++;
		ans[f[i].id] += query(up) - query(f[i].b);
	}
	for(register int i = l; i < j; i++) add(f[i].b , -1);
	j = r;
	for(register int i = mid; i >= l; i--)
	{
		while (f[j].a < f[i].a && j >= mid + 1) add(f[j].b , 1) , j--;
		ans[f[i].id] += query(up) - query(f[i].b);
	}
	for(register int i = j + 1; i <= r; i++) add(f[i].b , -1);
}

int main()
{
	scanf("%d%d" , &n , &m);
	for(register int i = 1; i <= n; i++) scanf("%d" , &f[i].a) , rd[f[i].a] = f[i].id = i;
	int x;
	for(register int i = 1; i <= m; i++) scanf("%d" , &x) , f[rd[x]].b = i;
	up = m;
	for(register int i = 1; i <= n; i++) 
	if (!f[i].b) f[i].b = ++up; else rd[f[i].b] = i;
	solve(1 , n);
	LL res = 0;
	for(register int i = 1; i <= n; i++) res += (LL)ans[i];
	for(register int i = 1; i <= m; i++) printf("%lld\n" , res) , res -= (LL)ans[rd[i]];
}