BZOJ3295 [Cqoi2011]动态逆序对 —— CDQ分治

题目链接:https://vjudge.net/problem/HYSBZ-3295

 

3295: [Cqoi2011]动态逆序对

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 6517  Solved: 2295
[Submit][Status][Discuss]

Description

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删
除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数

Input

输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。
以下n行每行包含一个1到n之间的正整数,即初始排列。
以下m行每行一个正整数,依次为每次删除的元素。
N<=100000 M<=50000

Output

 
输出包含m行,依次为删除每个元素之前,逆序对的个数。

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1
样例解释
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。

 

题解:

1.最核心的问题是:删除当前位置的数,会造成多少对逆序对的减少。

2.要统计删除当前数会造成多少对逆序对的减少,即需要统计:前面比它大的数的个数 + 后面比它小的数的个数 (前提是这些数没有被删除)。

3.由于题目还存在动态删除,则再为每个位置添加一个标志:Di,表明它是第几个被删除的。加上这个限制,就是一个三维偏序问题了。

4.以j为统计对象,sum[j]为删除位置j的数,所减少的逆序对。sum[j] = sum (i<j 且 Ai>Aj 且 Di>Dj)+ (j<i 且 Aj>Ai 且 Di>Dj)

5.得到sum数组之后,即知道删除当前位置的数,会造成多少对逆序对的减少。那么再算出初始的逆序对(不带删除,即二维偏序)即可。

 

写法一:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <vector>
  6 #include <cmath>
  7 #include <queue>
  8 #include <stack>
  9 #include <map>
 10 #include <string>
 11 #include <set>
 12 using namespace std;
 13 typedef long long LL;
 14 const int INF = 2e9;
 15 const LL LNF = 9e18;
 16 const int MOD = 1e9+7;
 17 const int MAXN = 1e5+100;
 18 
 19 struct node
 20 {
 21     int x, y, z;
 22 };
 23 node a[MAXN], b[MAXN], tmp[MAXN];
 24 
 25 int n, m, c[MAXN];
 26 int lowbit(int x) {return x&(-x);}
 27 void add(int x, int val) {for(int i=x;i<=n;i+=lowbit(i)) c[i]+=val;}
 28 int query(int x) {int ret=0; for(int i=x;i>0;i-=lowbit(i))ret+=c[i]; return ret;}
 29 
 30 int sum[MAXN], type;
 31 void CDQ(int l, int r)
 32 {
 33     if(l==r) return;
 34 
 35     int mid = (l+r)>>1;
 36     CDQ(l, mid); CDQ(mid+1, r);
 37     int p1 = l, p2 = mid+1;
 38     for(int i = l; i<=r; i++)
 39     {
 40         if(p2>r||(p1<=mid&&a[p1].y>=a[p2].y)) b[i] = a[p1++];
 41         else b[i] = a[p2++];
 42     }
 43     int cnt = 0;    //按删除顺序逆序排序了,所以先出现的更后删除
 44     for(int i = l; i<=r; i++)   //统计前面比它小的
 45     {
 46         a[i] = b[i];
 47         if(a[i].x<=mid) add(a[i].z, 1), cnt++;
 48         else if(a[i].y!=INF) sum[a[i].y] += cnt-query(a[i].z);
 49     }
 50     for(int i = l; i<=r; i++)
 51         if(a[i].x<=mid) add(a[i].z, -1);
 52 
 53     for(int i = l; i<=r; i++)   //统计后面比它大的
 54     {
 55         a[i] = b[i];
 56         if(a[i].x>mid) add(a[i].z, 1);
 57         else if(a[i].y!=INF) sum[a[i].y] += query(a[i].z-1);
 58     }
 59     for(int i = l; i<=r; i++)
 60         if(a[i].x>mid) add(a[i].z, -1);
 61 }
 62 
 63 int M[MAXN];
 64 int main()
 65 {
 66     while(scanf("%d%d", &n,&m)!=EOF)
 67     {
 68         for(int i = 1; i<=n; i++)
 69         {
 70             scanf("%d", &a[i].z);
 71             M[a[i].z] = i;
 72             a[i].x = i; a[i].y = INF;
 73         }
 74         for(int i = 1; i<=m; i++)
 75         {
 76             int del;
 77             scanf("%d", &del);
 78             a[M[del]].y = i;
 79         }
 80 
 81         LL ans = 0;
 82         memset(c, 0, sizeof(c));
 83         for(int i = 1; i<=n; i++)
 84         {
 85             ans += (i-1)-query(a[i].z);
 86             add(a[i].z, 1);
 87         }
 88 
 89         memset(c, 0, sizeof(c));
 90         memset(sum, 0, sizeof(sum));
 91         CDQ(1,n);
 92 
 93         printf("%lld\n", ans);
 94         for(int i = 1; i<m; i++)
 95         {
 96             ans -= sum[i];
 97             printf("%lld\n", ans);
 98         }
 99     }
100 }
View Code

 

写法二:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <vector>
  6 #include <cmath>
  7 #include <queue>
  8 #include <stack>
  9 #include <map>
 10 #include <string>
 11 #include <set>
 12 using namespace std;
 13 typedef long long LL;
 14 const int INF = 2e9;
 15 const LL LNF = 9e18;
 16 const int MOD = 1e9+7;
 17 const int MAXN = 1e5+100;
 18 
 19 struct node
 20 {
 21     int x, y, z;
 22 };
 23 node a[MAXN], b[MAXN];
 24 
 25 int n, m, c[MAXN];
 26 int lowbit(int x) {return x&(-x);}
 27 void add(int x, int val) {for(int i=x;i<=n;i+=lowbit(i)) c[i]+=val;}
 28 int query(int x) {int ret=0; for(int i=x;i>0;i-=lowbit(i))ret+=c[i]; return ret;}
 29 
 30 int sum[MAXN], type;
 31 void CDQ(int l, int r)
 32 {
 33     if(l==r) return;
 34 
 35     int mid = (l+r)>>1;
 36     CDQ(l, mid); CDQ(mid+1, r);
 37     int p1 = l, p2 = mid+1;
 38     for(int i = l; i<=r; i++)
 39     {
 40         if(p2>r||(p1<=mid&&a[p1].y>=a[p2].y)) b[i] = a[p1++];
 41         else b[i] = a[p2++];
 42     }
 43     int cnt = 0;
 44     for(int i = l; i<=r; i++)
 45     {
 46         a[i] = b[i];
 47         if(a[i].x<=mid) add(a[i].z, 1), cnt++;
 48         else if(a[i].y!=INF)
 49         {
 50             if(type) sum[a[i].y] += cnt-query(a[i].z);
 51             else sum[a[i].y] += query(a[i].z-1);
 52         }
 53     }
 54     for(int i = l; i<=r; i++)
 55         if(a[i].x<=mid) add(a[i].z, -1);
 56 }
 57 
 58 node tmp[MAXN];
 59 int M[MAXN];
 60 int main()
 61 {
 62     while(scanf("%d%d", &n,&m)!=EOF)
 63     {
 64         for(int i = 1; i<=n; i++)
 65         {
 66             scanf("%d", &a[i].z);
 67             M[a[i].z] = i;
 68             a[i].y = INF;
 69         }
 70         for(int i = 1; i<=m; i++)
 71         {
 72             int del;
 73             scanf("%d", &del);
 74             a[M[del]].y = i;
 75         }
 76 
 77         LL ans = 0;
 78         memset(c, 0, sizeof(c));
 79         for(int i = 1; i<=n; i++)
 80         {
 81             ans += (i-1)-query(a[i].z);
 82             add(a[i].z, 1);
 83         }
 84 
 85         memcpy(tmp, a, sizeof(tmp));
 86         for(int i = 1; i<=n; i++)
 87             a[i].x = i;
 88         memset(c, 0, sizeof(c));
 89         type = 1;
 90         CDQ(1,n);
 91 
 92         memcpy(a, tmp, sizeof(a));
 93         reverse(a+1,a+1+n);
 94         for(int i = 1; i<=n; i++)
 95             a[i].x = i;
 96         type = 0;
 97         CDQ(1,n);
 98 
 99         printf("%lld\n", ans);
100         for(int i = 1; i<m; i++)
101         {
102             ans -= sum[i];
103             printf("%lld\n", ans);
104         }
105     }
106 }
View Code

 

转载于:https://www.cnblogs.com/DOLFAMINGO/p/8660591.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值