Bzoj P3295 [Cqoi2011]动态逆序对___cdq分治套树状数组

15 篇文章 0 订阅
4 篇文章 0 订阅

题目大意:

在这里插入图片描述
在这里插入图片描述

分析:

对于每个数而言我存3个关键字
第一个是在初始序列中的位置 x i x_i xi
第二个是这个数的值 y i y_i yi
第三个是这个数在第几次操作时被删除,即 z i z_i zi,当这个数没被操作删除时, z i = m + 1 z_i=m+1 zi=m+1
那么我们考虑离线做,
求出初始序列的逆序对数,为 s u m sum sum
对于第 i i i次操作而言,假如删掉的是 x x x x x x在初始序列中的位置是 p o s pos pos
那么删掉这个数给 s u m sum sum带来的代价就是
所有满足 x i &lt; p o s , y i &gt; x , z i &gt; i x_i&lt;pos,y_i&gt;x,z_i&gt;i xi<posyi>xzi>i的个数+所有满足 x i &gt; p o s , y i &lt; x , z i &gt; i x_i&gt;pos,y_i&lt;x,z_i&gt;i xi>pos,yi<x,zi>i的个数
那么这个就是三维偏序辣
二遍 cdq分治套树状数组就没了

代码:

#include <iostream>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>

#define rep(i, st, ed) for (int i = st; i <= ed; i++)
#define rwp(i, ed, st) for (int i = ed; i >= st; i--)
#define mt(x) memset(x, 0, sizeof(x))

#define N 200005

using namespace std;

typedef long long ll;

struct Node {
	int x, y, mark;
}C[N], tmp[N];
int orz[N], zyf[N], n, m, cdp, tot;
ll ans[N], sum;

void Ins(int x, int y) {
	for (; x <= m + 1; x += (x & (-x))) zyf[x] += y;
}

int Cal(int x) {
	int rp = 0;
	for (; x; x -= (x & (-x))) rp += zyf[x];
	return rp;
}

void cdq(int L, int R) {
	if (R - L <= 1) return;
	int mid = (L + R) >> 1;
	cdq(L, mid);
	cdq(mid, R);
	int p = L, q = mid, cnt = 0;	
	while (p < mid && q < R) {
		if (C[p].y > C[q].y) { 
		   Ins(C[p].mark, 1); tmp[++cnt] = C[p++]; 
		} else {
			 sum = sum + 1ll * (p - L);
		   	 if (C[q].mark <= m) ans[C[q].mark] = ans[C[q].mark] + 1ll * (Cal(m + 1) - Cal(C[q].mark));
		   	 tmp[++cnt] = C[q++];
		   } 
	}
	while (q < R) {	
		sum = sum + 1ll * (p - L); 
		if (C[q].mark <= m) ans[C[q].mark] = ans[C[q].mark] + 1ll * (Cal(m + 1) - Cal(C[q].mark));
		tmp[++cnt] = C[q++];	
	}
	rep(i, L, p - 1) Ins(C[i].mark, -1);
	while (p < mid) tmp[++cnt] = C[p++];
	rep(i, 1, cnt) C[L + i - 1] = tmp[i];
}

bool cmp(Node aa, Node bb) {
	return aa.x > bb.x;
}


void cdq1(int L, int R) {
	if (R - L <= 1) return;
	int mid = (L + R) >> 1;
	cdq1(L, mid);
	cdq1(mid, R);
	int p = L, q = mid, cnt = 0;	
	while (p < mid && q < R) {
		if (C[p].y < C[q].y) { 
		   Ins(C[p].mark, 1); tmp[++cnt] = C[p++]; 
		} else {
		   	 if (C[q].mark <= m) ans[C[q].mark] = ans[C[q].mark] + 1ll * (Cal(m + 1) - Cal(C[q].mark));
		   	 tmp[++cnt] = C[q++];
		   } 
	}
	while (q < R) {	
		if (C[q].mark <= m) ans[C[q].mark] = ans[C[q].mark] + 1ll * (Cal(m + 1) - Cal(C[q].mark));
		tmp[++cnt] = C[q++];	
	}
	rep(i, L, p - 1) Ins(C[i].mark, -1);
	while (p < mid) tmp[++cnt] = C[p++];
	rep(i, 1, cnt) C[L + i - 1] = tmp[i];
}

int main() {
	scanf("%d %d", &n, &m); 
	rep(i, 1, n) C[i].x = i, scanf("%d", &C[i].y);
	int x;
	rep(i, 1, m) scanf("%d", &x), orz[x] = i;
	rep(i, 1, n) 
	    if (orz[C[i].y]) C[i].mark = orz[C[i].y]; else C[i].mark = m + 1;
	cdq(1, n + 1); 
	sort(C + 1, C + n + 1, cmp);
	cdq1(1, n + 1);
	rep(i, 1, m) {
		printf("%lld\n", sum);
		sum = sum - ans[i];
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值