P3157 [CQOI2011]动态逆序对【CDQ分治-三维带修改】

36 篇文章 0 订阅
5 篇文章 0 订阅

传送门

现在给出 1 ∼ n 1 \sim n 1n 的一个排列,按照某种顺序依次删除 m m m 个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

分析

CDQ分治-三维偏序
【操作时间】【数字下标】【数值】
操作时间优先级高,必然排在第一维,对操作时间进行排序
【数字下标】和【数值】优先级相同,任选一个进行分治
考虑删除的操作
删除操作会给逆序对带来的影响怎么来的?

  • 在删除操作时间前 前面 存在的大的
  • 在删除操作时间前 后面 存在的小的

分治不能初始化,将初始的值按照插入进行分治
时间顺序排好,初始化插入操作在前,删除操作依次放

对【数组下标】进行排序,统计前面的大数后面的小数

细节:
注意到统计小数的时候是左右区间都是从后往前的。为什么呢?
因为在统计前分治左右的时候已经将【数组下标】排好序了
倒过来是为了求,后面(下标大)的小数
这里思考一下,应该就没什么问题了
类似题:CDQ分治专栏

代码

代码通过情况

初代版本AC

//P3157
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;

int ar[MAX_N];

int tr[MAX_N];
inline int lowbit(int x) {
	return x & -x;
}
inline void add(int x, int k) {
	assert(x!=0);
	while (x <= N) {
		tr[x] += k;
		x += lowbit(x);
	}
}

inline int ask(int x) {
	int res = 0;
	while (x) {
		res += tr[x];
		x -= lowbit(x);
	}
	return res;
}

struct Qr {
	int x, pos, t, v;
}qr[MAX_N];
int sa[MAX_N];

int ans[MAX_N];

void CDQ(int l, int r) {
	if (l == r) return;
	int mid = l + ((r-l)>>1);
	CDQ(l, mid);CDQ(mid+1, r);
	
	int lx = l, rx = mid+1;
	for (int i = l; i <= r; ++i) {
		if ((lx <= mid && qr[lx].pos <= qr[rx].pos) || rx > r) {
			add(qr[lx].x, qr[lx].v);
			++lx;
		} else {
			ans[qr[rx].t] += qr[rx].v * (ask(N) - ask(qr[rx].x));
			++rx;
		}
	}
	lx = l, rx = mid+1;
	for (int i = l; i <= r; ++i) {
		if ((lx <= mid && qr[lx].pos <= qr[rx].pos) || rx > r) {
			add(qr[lx].x, -qr[lx].v);
			++lx;
		} else {
			++rx;
		}
	}
	
	lx = mid, rx = r;
	for (int i = l; i <= r; ++i) {
		if ((lx >= l && qr[lx].pos >= qr[rx].pos) || rx <= mid) {
			add(qr[lx].x, qr[lx].v);
			--lx;
		} else {
			ans[qr[rx].t] += qr[rx].v * (ask(qr[rx].x-1));
			--rx;
		}
	}
	lx = mid, rx = r;
	for (int i = l; i <= r; ++i) {
		if ((lx >= l && qr[lx].pos >= qr[rx].pos) || rx <= mid) {
			add(qr[lx].x, -qr[lx].v);
			--lx;
		} else {
			--rx;
		}
	}
	sort(qr+l, qr+r+1, [](Qr a, Qr b){return a.pos < b.pos;});	
}

void solve(){
	sc("%lld%lld", &N, &M);
	int cnt = 0;
	int x;
	for (int i = 1; i <= N; ++i) {
		sc("%lld", &x);
		qr[++cnt] = {x, i, cnt, 1};
		sa[x] = i;
	}
	
	for (int i = 1; i <= M; ++i) {
		sc("%lld", &x);
		qr[++cnt] = {x, sa[x], cnt, -1};
	}
	
	CDQ(1, cnt);
	
	for (int i = 1; i <= cnt; ++i) {
		ans[i] += ans[i-1];
	}
	for (int i = cnt-M; i < cnt; ++i) {
		cout << ans[i] << "\n";
	}
}

signed main()
{
	#ifndef ONLINE_JUDGE
	//FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();

	return AC;
}

优化排序,树状数组

//P3157
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;

int ar[MAX_N];

int tr[MAX_N];
int span[MAX_N];
int tim = 0;

inline int lowbit(int x) {
	return x & -x;
}

inline void add(int x, int k) {
	assert(x!=0);
	while (x <= N) {
	    if (span[x] != tim) {
	        span[x] = tim;
	        tr[x] = 0;
	    }
		tr[x] += k;
		x += lowbit(x);
	}
}

inline int ask(int x) {
	int res = 0;
	while (x) {
		res += span[x] == tim ? tr[x] : 0;
		x -= lowbit(x);
	}
	return res;
}

struct Qr {
	int x, pos, t, v;
}qr[MAX_N], L[MAX_N], R[MAX_N];
int sa[MAX_N];

int ans[MAX_N];

void CDQ(int l, int r) {
	if (l == r) return;
	int mid = l + ((r-l)>>1);
	CDQ(l, mid);CDQ(mid+1, r);
	
	++tim;
	int lx = l, rx = mid+1;
	int ls = 0;
	int rs = 0;
	for (int i = l; i <= r; ++i) {
		if ((lx <= mid && qr[lx].pos <= qr[rx].pos) || rx > r) {
			add(qr[lx].x, qr[lx].v);
			L[++ls] = qr[lx++];
		} else {
			ans[qr[rx].t] += qr[rx].v * (ask(N) - ask(qr[rx].x));
			R[++rs] = qr[rx++];
		}
	}
	
    ++tim;
	lx = mid, rx = r;
	for (int i = l; i <= r; ++i) {
		if ((lx >= l && qr[lx].pos >= qr[rx].pos) || rx <= mid) {
			add(qr[lx].x, qr[lx].v);
			--lx;
		} else {
			ans[qr[rx].t] += qr[rx].v * (ask(qr[rx].x-1));
			--rx;
		}
	}
	
	for (int i = 1; i <= ls; ++i) {
	    qr[l+i-1] = L[i];
	}
	for (int i = 1; i <= rs; ++i) {
	    qr[l+ls+i-1] = R[i];
	}
}

void solve(){
	sc("%lld%lld", &N, &M);
	int cnt = 0;
	int x;
	for (int i = 1; i <= N; ++i) {
		sc("%lld", &x);
		qr[++cnt] = {x, i, cnt, 1};
		sa[x] = i;
	}
	
	for (int i = 1; i <= M; ++i) {
		sc("%lld", &x);
		qr[++cnt] = {x, sa[x], cnt, -1};
	}
	
	CDQ(1, cnt);
	
	for (int i = 1; i <= cnt; ++i) {
		ans[i] += ans[i-1];
	}
	for (int i = cnt-M; i < cnt; ++i) {
		cout << ans[i] << "\n";
	}
}

signed main()
{
	#ifndef ONLINE_JUDGE
	//FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();

	return AC;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hexrt

客官,请不要给我小费!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值