P3157 [CQOI2011]动态逆序对

Posted 斗奋力努

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3157 [CQOI2011]动态逆序对相关的知识,希望对你有一定的参考价值。

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 分治)

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

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

「CQOI2011」动态逆序对