洛谷P3157 cdq解法

题意:

给出一个 n n n的排列,有 m m m次删除操作,每次删除其中一个元素 x x x,输出每次删除前序列的逆序对个数

cdq分治法:

与主席树相同的是,我们都是要删除一个位置时,这个位置所构成的逆序对个数,换句话说,就是求这个位置前面有多少大于 x x x的,后面有多少小于 x x x的。主席树能在线处理,动态修改权值线段树,使得某个区间的权值线段树任意查询,所以主席树可以在线处理这个问题。而换个角度思考,逆序对本身就是一种二维偏序关系,那在这里删除 x x x,能产生影响的一定是符合逆序对偏序关系的,即位置在前的比自己大的,位置在后比自己小的。但是仅仅按照这样子计算出删除某个位置的影响不能双向修改,比如 x x x y y y是一对逆序对,我们知道 x x x的影响是1,删去 x x x y y y的影响数也要减1。实际上删去 x x x能少去多少逆序对仅跟满足偏序条件和当前仍然存在的元素有关系,所以给元素多加一个维度的属性,删去时间

那么要求的就是满足如下偏序关系的对数了:

t [ i ] t[i] t[i] i i i元素删去的时间, v a l [ i ] val[i] val[i] i i i元素的权值, p o s [ i ] pos[i] pos[i] i i i元素的位置,在 t [ i ] < t [ j ] t[i]<t[j] t[i]<t[j]的条件下,符合

v a l [ j ] > v a l [ i ] , p o s [ j ] < p o s [ i ] val[j]>val[i],pos[j]<pos[i] val[j]>val[i],pos[j]<pos[i]

v a l [ j ] < v a l [ i ] , p o s [ j ] > p o s [ i ] val[j]<val[i],pos[j]>pos[i] val[j]<val[i],pos[j]>pos[i]

至此转换为一个三维偏序问题了,对两组偏序关系分别 c d q cdq cdq分治即可

一些细节:

存在一些元素不被删去,那么一个办法是令不被删去的元素的删去时间是无穷大即可

需要求出一开始的逆序对数,一个方法是在输入的时候二维偏序求解逆序对,另一个方法是令不被删去的元素的删去时间分别是 [ ∞ , ∞ + 1 , ∞ + 2 , . . . . . ] [\infty,\infty+1,\infty+2,.....] [,+1,+2,.....],在设置为无穷大时把他们的顺序设为依次,所有位置的答案加起来就是逆序对个数

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int n;

struct fenwick
{
    int tree1[200005],tree2[200005];
    inline int lowbit(int x){return -x&x;} 
    void add(int x,int v)
    {
        int tmp=x*v;
        while(x<=n)
        {
            tree1[x]+=v;
            tree2[x]+=tmp;
            x+=lowbit(x);
        }
    }
    void add(int l,int r,int k)
    {
        add(l,k);
        add(r+1,-k);
    }
    int getsum(int l,int r)
    {
        int ret=0,x=r;
        while(x)
        {
            ret+=(r+1)*tree1[x]-tree2[x];
            x-=lowbit(x);
        }
        x=l-1;
        while(x)
        {
            ret-=l*tree1[x]-tree2[x];
            x-=lowbit(x);
        }
        return ret;
    }
}tree;

struct node
{
    int val,t,pos;
}a[200005],tmp[200005];

int m,del[200005],pos[200005],ans[200005];

void solve1(int l,int r)
{
    if(l==r) return;
    int mid=l+r>>1;
    solve1(l,mid); solve1(mid+1,r);
    int j=l,i=mid+1,k=l;
    while(j<=mid&&i<=r)
    {
        if(a[j].pos<=a[i].pos)
        {
            tree.add(a[j].val,a[j].val,1);
            tmp[k++]=a[j++];
        }
        else
        {
            ans[a[i].pos]+=tree.getsum(a[i].val,n);
            tmp[k++]=a[i++];
        }
    }
    while(j<=mid)
    {
        tree.add(a[j].val,a[j].val,1);
        tmp[k++]=a[j++];
    }
    while(i<=r)
    {
        ans[a[i].pos]+=tree.getsum(a[i].val,n);
        tmp[k++]=a[i++];
    }
    for(int i=l;i<=mid;i++) tree.add(a[i].val,a[i].val,-1);
    for(int i=l;i<=r;i++) a[i]=tmp[i];
}

void solve2(int l,int r)
{
    if(l==r) return;
    int mid=l+r>>1;
    solve2(l,mid); solve2(mid+1,r);
    int j=l,i=mid+1,k=l;
    while(j<=mid&&i<=r)
    {
        if(a[j].pos>=a[i].pos)
        {
            tree.add(a[j].val,a[j].val,1);
            tmp[k++]=a[j++];
        }
        else
        {
            ans[a[i].pos]+=tree.getsum(1,a[i].val);
            tmp[k++]=a[i++];
        }
    }
    while(j<=mid)
    {
        tree.add(a[j].val,a[j].val,1);
        tmp[k++]=a[j++];
    }
    while(i<=r)
    {
        ans[a[i].pos]+=tree.getsum(1,a[i].val);
        tmp[k++]=a[i++];
    }
    for(int i=l;i<=mid;i++) tree.add(a[i].val,a[i].val,-1);
    for(int i=l;i<=r;i++) a[i]=tmp[i];
}

ll tot;
int q[200005];

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i].val);
        a[i].pos=i;
        pos[a[i].val]=i;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&q[i]);
        a[pos[q[i]]].t=i;
    };
    int pp=0x3fffffff;
    for(int i=1;i<=n;i++)
        if(!a[i].t) a[i].t=++pp;
    sort(a+1,a+1+n,[&](const node& x,const node& y){
        if(x.t!=y.t) return x.t>y.t;
        if(x.pos!=y.pos) return x.pos<y.pos;
        return x.val>y.val;
    }); 
    solve1(1,n);
    sort(a+1,a+1+n,[&](const node& x,const node& y){
        if(x.t!=y.t) return x.t>y.t;
        if(x.pos!=y.pos) return x.pos>y.pos;
        return x.val<y.val;
    }); 
    solve2(1,n);
    for(int i=1;i<=n;i++) tot+=ans[i];
    for(int i=1;i<=m;i++)
    {
        printf("%lld\n",tot);
        tot-=ans[pos[q[i]]];
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值