luogu P3157 [CQOI2011]动态逆序对(CDQ分治)

题目描述

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

输入输出格式

输入格式:

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

输出格式:

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

题解

我们发现一个数的贡献,就是就是t‘<t(删除时间),xb‘<xb(下标),w‘>w(权值)的数的数量和t‘>t,xb‘>xb,w‘<w的数的数量之和。

这就是一个三维偏序类型的题,所以做两遍CDQ分治分别的到这两种贡献。最后用总逆序对数减去就好了。

1A真开心。。。

  1 #include<iostream>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<cstdio>
  5 #include<cmath>
  6 using namespace std;
  7 const long long N=100100;
  8 long long n,m,a[N],b[N],tmp,tr[N],ans[N],book[N],ma[N],tot;
  9 struct query{
 10     long long id,xb,w;
 11 }q[N],c[N];
 12 bool cmp(query a,query b){
 13     return a.id>b.id;
 14 }
 15 void gb(long long l,long long r){
 16     if(l==r)return;
 17     long long mid=(l+r)>>1;
 18     gb(l,mid);
 19     gb(mid+1,r);
 20     long long ll=l;
 21     long long lr=mid+1;
 22     long long cnt=0;
 23     while(ll<=mid&&lr<=r){
 24         cnt++;
 25         if(a[ll]<a[lr]){
 26             b[cnt]=a[ll++];
 27         }
 28         else{
 29             b[cnt]=a[lr++];
 30             tmp+=mid-ll+1;
 31         }
 32     }
 33     while(ll<=mid)b[++cnt]=a[ll++];
 34     while(lr<=r)b[++cnt]=a[lr++];
 35     for(long long i=l;i<=r;i++){
 36         a[i]=b[i-l+1];
 37     }
 38 }
 39 long long lowbit(long long x){
 40     return x&-x;
 41 }
 42 void add(long long x,long long w){
 43     for(long long i=x;i<=n;i+=lowbit(i)){
 44         tr[i]+=w;
 45     }
 46 }
 47 long long getsum(long long x){
 48     long long ans=0;
 49     for(long long i=x;i>=1;i-=lowbit(i)){
 50         ans+=tr[i];
 51     }
 52     return ans;
 53 }
 54 void cdq(long long l,long long r){
 55     if(l==r)return;
 56     long long mid=(l+r)>>1;
 57     cdq(l,mid);cdq(mid+1,r);
 58     long long ll=l;long long rl=mid+1;long long now=0;
 59     while(ll<=mid&&rl<=r){
 60         if(q[ll].xb<q[rl].xb){
 61             add(q[ll].w,1);
 62             c[++now]=q[ll++];
 63         }
 64         else{
 65             ans[q[rl].id]+=getsum(n)-getsum(q[rl].w);
 66             c[++now]=q[rl++];
 67         }
 68     }
 69     while(ll<=mid){
 70         add(q[ll].w,1);
 71         c[++now]=q[ll++];
 72     }
 73     while(rl<=r){
 74         ans[q[rl].id]+=getsum(n)-getsum(q[rl].w);
 75         c[++now]=q[rl++];
 76     }
 77     for(long long i=l;i<=mid;i++)add(q[i].w,-1);
 78     for(long long i=l;i<=r;i++)q[i]=c[i-l+1];
 79 }
 80 void CDQ(long long l,long long r){
 81     if(l==r)return;
 82     long long mid=(l+r)>>1;
 83     CDQ(l,mid);CDQ(mid+1,r);
 84     long long ll=l;long long rl=mid+1;long long now=0;
 85     while(ll<=mid&&rl<=r){
 86         if(q[ll].xb>q[rl].xb){
 87             add(q[ll].w,1);
 88             c[++now]=q[ll++];
 89         }
 90         else{
 91             ans[q[rl].id]+=getsum(q[rl].w);
 92             c[++now]=q[rl++];
 93         }
 94     }
 95     while(ll<=mid){
 96         add(q[ll].w,1);
 97         c[++now]=q[ll++];
 98     }
 99     while(rl<=r){
100         ans[q[rl].id]+=getsum(q[rl].w);
101         c[++now]=q[rl++];
102     }
103     for(long long i=l;i<=mid;i++)add(q[i].w,-1);
104     for(long long i=l;i<=r;i++)q[i]=c[i-l+1];
105 }
106 int main(){
107     scanf("%lld%lld",&n,&m);
108     for(long long i=1;i<=n;i++){
109         scanf("%lld",&a[i]);
110         ma[a[i]]=i;
111     }
112     for(long long i=1;i<=m;i++){
113         long long x;
114         scanf("%lld",&x);
115         q[i].id=i;q[i].xb=ma[x];q[i].w=x;
116         book[ma[x]]=1;
117     }
118     tot=m;
119     for(long long i=1;i<=n;i++){
120         if(book[i]==0){
121             q[++tot].id=m+1;q[tot].xb=i;q[tot].w=a[i];
122         }
123     }
124     sort(q+1,q+n+1,cmp);
125     cdq(1,n);
126     sort(q+1,q+n+1,cmp);
127     CDQ(1,n);
128     gb(1,n);
129     for(long long i=1;i<=m;i++){
130         printf("%lld\n",tmp);
131         tmp-=ans[i];
132     }
133     return 0;
134 }

原文地址:https://www.cnblogs.com/Xu-daxia/p/9460134.html

时间: 2024-10-08 15:41:37

luogu P3157 [CQOI2011]动态逆序对(CDQ分治)的相关文章

LUOGU P3157 [CQOI2011]动态逆序对(CDQ 分治)

传送门 解题思路 cdq分治,将位置看做一维,修改时间看做一维,权值看做一维,然后就转化成了三维偏序,用排序+cdq+树状数组.注意算删除贡献时要做两次cdq,分别算对前面和后面的贡献. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 100005; const int MAXM =

P3157 [CQOI2011]动态逆序对(CDQ分治)

题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. 输入格式 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. 输出格式 输出包含m行,依次为删除每个元素之前,逆序对的个数. 输入输出样例 输入 #1复制 5 4 1 5 3 4 2 5 1

P3157 [CQOI2011]动态逆序对 (CDQ解决三维偏序问题)

P3157 [CQOI2011]动态逆序对 题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. 输入格式 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. 输出格式 输出包含m行,依次为删除每个元素之前,逆序对的个数. 输入输出样例 输入

【BZOJ3295】[Cqoi2011]动态逆序对 cdq分治

[BZOJ3295][Cqoi2011]动态逆序对 Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. Output 输出包含m行,依次为删除每个元素之前,逆序对的

[Luogu P3157][CQOI2011]动态逆序对 (树套树)

题面 传送门:[CQOI2011]动态逆序对 Solution 一开始我看到pty巨神写这套题的时候,第一眼还以为是个SB题:这不直接开倒车线段树统计就完成了吗? 然后冷静思考了一分钟,猛然发现单纯的线段树并不能解决这个问题,好像还要在外面再套上一颗树. 这就很shit了.你问我资磁不资磁树套树,我是不资磁的,树套树是暴力数据结构,我能资磁吗? 很不幸,昨天现实狠狠地打了我一脸:时间不够开新坑的,不切题又浑身难受,找了半天题,还是把这道题拉了出来(哈,真香) 不扯淡了,这题还是很显然的. 考虑开

BZOJ 3295: [Cqoi2011]动态逆序对 cdq分治

https://www.lydsy.com/JudgeOnline/problem.php?id=3295 这个妹妹我曾见过的~~~ 之前应该在校内oj写了,似乎还写过题解?发现没写博客就重新水一遍代码水一篇博客好了. 把找逆序对的过程想成一个一个往里塞数字然后找每个数字可以组成的逆序对,把最后要去掉的几个也想成往里塞,所以加一个时间维度用cdq求三维偏序. 1 #include<iostream> 2 #include<cstdio> 3 #include<algorith

P3157 动态逆序对 CDQ分治

动态逆序对 CDQ分治 传送门:https://www.luogu.org/problemnew/show/P3157 题意: 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. 题解: 这个题是告诉你如何将一个问题转换为三维偏序问题 首先,求解逆序对,那么a.val>b.val,删除一个元素的时间是t,a.t<b.t,这个元素对应的原序列中的位置为

P3157 [CQOI2011]动态逆序对

P3157 [CQOI2011]动态逆序对 https://www.luogu.org/problemnew/show/P3157 题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. 输入输出格式 输入格式: 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次

题解-P3157 [CQOI2011]动态逆序对

树状数组套线段树,用动态开点线段树.此类就这一题放在这吧. 程序写的太丑了,不要介意. #include<iostream> #include<cstdio> using namespace std; typedef long long i64; const int N=1e5+5; const int S=1e7+7; int a[N],p[N]; int q[N]; bool b[N]; int r[N]; int cnt,x[S],c[S][2]; i64 ans[N]; v