P3157 [CQOI2011]动态逆序对
Posted 斗奋力努
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3157 [CQOI2011]动态逆序对相关的知识,希望对你有一定的参考价值。
前言:
本题有很多种写法:分块,树状数组套线段树,kdtree等等,但这里采用CDQ分治的方法来解决问题。本文只会给出CDQ分治的解题思路和代码,详解解释可以去看看其他博文。
题目:
默认看到这里的聚聚们都会CDQ分治了,下面给出思路和代码
//思路:
// ①:题目要求我们求动态逆序对,同时题目又出现在cdq分治里面,自然而然地想到要用CDQ分治去作答
// 开始我们就已知了位置、价值这两维。此时还缺少第三维,看到题目是动态带修改的,自然想到可以
// 采用时间为我们的第三维。此时我们就已经完成了解题的一大部分,三维对应(时间,位置,价值)
// (记得保证时间全部不同,下面有处理)
// ②:解决了三维关系后,我们剩下的就是考虑如果去计算每一次的答案。显然我们知道越晚被删除的或不被
// 删除的元素对答案做贡献的次数会越多,(这里反过来记录时间)当我们按照时间升序排序解决第一维时,
// 同时解决消除时间对答案的影响
// ③:最终的计算答案时,因为我们要考虑两种情况:(1)在其前面比其大 (2)在其后面比其小
// 就是本题的重点部分:我们先将其按照第一关键字位置,第二关键字价值去cek。每次为真时,将左边i的价值
// 记录到树状数组中去;为假,那么就可以统计右边j的时间可以得到上面(1)部分的答案
// (记录归并排序要把该次排序后顺序记录,同时还有清空左边在树状数组中的影响)
// ④:计算上面(2)部分的答案,此时只需要我们从r往l去扫一遍,同时记录贡献就行了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e5+6;
ll n,m;
ll p[N],ans[N];
struct node{
ll ti,pos,val;
bool operator<(const node& t) const{
return ti<t.ti;
}
}q[N],te[N];
ll tr[N];
ll lowbit(ll x){return x&(-x);}
void add(ll x,ll v){//修改
for(ll i=x;i<N;i+=lowbit(i)) tr[i]+=v;
}
void clear(ll x){//清空
for(ll i=x;i<N;i+=lowbit(i)) tr[i]=0;
}
ll query(ll x){//查询
ll res=0;
for(ll i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
bool cek(node l,node r){//先求在其前面比其小的个数
if(l.pos==r.pos) return l.val<r.val;
return l.pos<r.pos;
}
void cdq(ll l,ll r){
if(l>=r) return;
ll mid=(l+r)>>1;
cdq(l,mid),cdq(mid+1,r);
ll i=l,j=mid+1,k=0;
while(i<=mid&&j<=r){
if(cek(q[i],q[j])) add(q[i].val,1),te[k++]=q[i++];
else ans[q[j].ti]+=query(n)-query(q[j].val),te[k++]=q[j++];//(1)部分贡献
}
while(i<=mid) add(q[i].val,1),te[k++]=q[i++];//走完左边
while(j<=r) ans[q[j].ti]+=query(n)-query(q[j].val),te[k++]=q[j++];//走完右边
for(ll i=l;i<=mid;i++) clear(q[i].val);//清空左边对树状数组的影响
for(ll i=l,j=0;j<k;i++,j++) q[i]=te[j];//归并排序后
for(ll i=r;i>=l;i--){//(2)部分贡献
if(q[i].ti<=mid) add(q[i].val,1);
else ans[q[i].ti]+=query(q[i].val);
}
for(ll i=l;i<=r;i++) clear(q[i].val);
}
int main(){
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++){
ll x;scanf("%lld",&x);p[x]=i;
q[i]={0,i,x}; //初始q[i].t=0代表后续没有删除
}
ll sumt=n;
for(ll i=1;i<=m;i++){
ll x;scanf("%lld",&x);
q[p[x]].ti=sumt--;//此时q[p[x]].t!=0了,赋值一个当前剩余最大时间
}
for(ll i=1;i<=n;i++){
if(!q[i].ti) q[i].ti=sumt--;//对后续没有删除的值赋一个当前剩余最大时间(自证不难)
}
sort(q+1,q+n+1);
cdq(1,n);
for(ll i=1;i<=n;i++) ans[i]+=ans[i-1];//求的就是前缀和
for(ll i=n;i>=n-m+1;i--) printf("%lld\\n",ans[i]);
return 0;
}
以上是关于P3157 [CQOI2011]动态逆序对的主要内容,如果未能解决你的问题,请参考以下文章
洛谷 P3157 [CQOI2011]动态逆序对 | CDQ分治
[Luogu P3157][CQOI2011]动态逆序对 (树套树)
LUOGU P3157 [CQOI2011]动态逆序对(CDQ 分治)