zoukankan      html  css  js  c++  java
  • 【ybt金牌导航6-5-4】【luogu P3157】动态逆序对(CDQ分治)(树状数组)

    动态逆序对

    题目链接:ybt金牌导航6-5-4 / luogu P3157

    题目大意

    给你一个排列,每次会删去一些数,然后问你每次删去前这个数组的逆序对数。

    思路

    首先如果没有删去,我们就直接一个树状数组就好了。

    但是它会不断删去,那你考虑删去一个数的时候贡献会减少多少。
    那我们分两种情况,一种是在逆序对的前面,一种是在逆序对的后面。

    那我们想,假设现在要删的点位置是 (p_i),大小是 (a_i),被删的时间是 (t_i)。(如果没有被删就是 (m+1)

    那如果是在逆序对的前面,你就是要找点 (j),然后满足:
    (p_i<p_j,a_i>a_j,t_i<t_j)

    如果是在逆序对的后面,你就是要找点 (j),然后满足:
    (p_i>p_j,a_i<a_j,t_i<t_j)

    然后两个三维偏序,就用两个 CDQ 分治就好了。

    其实一个就好,另一个就是第一个把 (p_x,a_x) 取反再做一次就好了。

    代码

    #include<cstdio>
    #include<algorithm>
    #define ll long long
    
    using namespace std;
    
    struct node {
    	int i, a, dt;
    }q[100001];
    int n, m, a[100001];
    int b[50001], pl[100001];
    ll ans[50001];
    
    struct sjsz {//树状数组
    	int v[100001];
    	
    	void insert(int x, int y) {
    		for (; x <= n; x += x & (-x))
    			v[x] += y;
    	}
    	
    	int query(int x) {
    		int re = 0;
    		for (; x; x -= x & (-x))
    			re += v[x];
    		return re;
    	}
    }T;
    
    bool cmp(node x, node y) {
    	return x.i < y.i;
    }
    
    bool cmp1(node x, node y) {
    	return x.a < y.a;
    }
    
    void cdq(int l, int r) {//CDQ 分治
    	if (l == r) return;
    	int mid = (l + r) >> 1;
    	cdq(l, mid); cdq(mid + 1, r);
    	
    	sort(q + l, q + mid + 1, cmp1);
    	sort(q + mid + 1, q + r + 1, cmp1);
    	
    	int nowl = l, nowr = mid + 1;
    	while (nowl <= mid) {
    		while (nowr <= r && q[nowl].a > q[nowr].a) {
    			T.insert(q[nowr].dt, 1);
    			nowr++;
    		}
    		ans[q[nowl].dt] += (nowr - (mid + 1)) - T.query(q[nowl].dt);
    		nowl++;
    	}
    	
    	for (int i = mid + 1; i < nowr; i++)
    		T.insert(q[i].dt, -1);
    }
    
    int main() {
    	scanf("%d %d", &n, &m);
    	for (int i = 1; i <= n; i++) scanf("%d", &a[i]), q[i].a = a[i], q[i].i = i, pl[a[i]] = i;
    	for (int i = 1; i <= m; i++) {
    		scanf("%d", &b[i]); q[pl[b[i]]].dt = i;
    	}
    	
    	for (int i = 1; i <= n; i++)
    		if (!q[i].dt) q[i].dt = m + 1;
    	
    	for (int i = 1; i <= n; i++) {//计算一开始的逆序对个数
    		T.insert(a[i], 1);
    		ans[0] += 1ll * (i - T.query(a[i]));
    	}
    	for (int i = 1; i <= n; i++) {
    		T.insert(a[i], -1);
    	}
    	
    	//计算每次少了这个数会减少多少贡献
    	cdq(1, n);//要找另一个在它后面的
    	for (int i = 1; i <= n; i++)
    		q[i].i *= -1, q[i].a *= -1;
    	sort(q + 1, q + n + 1, cmp);
    	cdq(1, n);//要找另一个在它前面的
    	
    	for (int i = 0; i < m; i++) {
    		printf("%lld
    ", ans[i]);
    		ans[i + 1] = ans[i] - ans[i + 1];//不断减去每次减少的
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    [51nod] 1088 最长回文子串 #Hash+二分
    [51nod] 1378 夹克老爷的愤怒 #树形DP
    [BZOJ] 2456: mode #众数计数法
    [51nod] 1199 Money out of Thin Air #线段树+DFS序
    [51nod] 1494 选举拉票 #算法设计策略
    [51nod] 1463 找朋友 #离线+扫描线
    [BZOJ] 2733: [HNOI2012]永无乡 #线段树合并+并查集
    [BZOJ] 1012: [JSOI2008]最大数maxnumber
    [Codeforces] E. Lomsat gelral #DSU on Tree
    [BZOJ] 4756: [Usaco2017 Jan]Promotion Counting #线段树合并+权值线段树
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_P3157.html
Copyright © 2011-2022 走看看