[BZOJ3295][Cqoi2011]动态逆序对(CDQ分治||树套树)

题目:

我是超链接

题解:

记得很久以前给舒老师看这道题,他一眼秒了然后写了出来,给我造成了极大的心理创伤。。

cdq分治

首先可以考虑把这个处理过程倒过来,因为删数我们不擅长,但是添加是ok的
那么问题变成:一开始有一些数(全删完后剩的),求逆序对然后每添加一个数求一次逆序对
我们很快就可以发现这个题目的三个关键词:位置,时间,大小
三维偏序!
对于一个三元组(a,b,c),有两组满足“逆序对”的条件
a2 < a && b2 < b && c2 > c
a2 < a && b2 > b && c2 < c
那么我们要分开来加,而且因为添加的先后性并不会加重
那么我们对于时间排序,位置分治,这都很套路,加值的时候维护对于每个值在ta要消失时刻产生的逆序对贡献,那么我们输出的时候就要对于时间累加输出

树套树(挖坑

代码:

cdq分治

#include <cstdio>
#include <iostream>
#include <algorithm>
#define LL long long 
using namespace std;
const int N=100005;
struct hh{int z,wz,tim;LL ans;}f[N],jl[N];
int n,m,a[N],in[N];LL c[N];bool ok[N];
void add(int loc,int val)
{
    for (int i=loc;i<=n;i+=i&(-i)) 
      c[i]+=val;
}
LL qurry(int loc)
{
    LL ans=0;
    for (int i=loc;i>=1;i-=i&(-i)) ans+=c[i];
    return ans;
}
void cdq(int l,int r)//已经按照时间排好,就看位置+树状数组的大小啦 
{
    if (l>=r) return;
    int mid=(l+r)>>1;
    cdq(l,mid); cdq(mid+1,r);
    int t1=l,t2=mid+1,all=0;
    for (int i=l;i<=r;i++)
      if ((t1<=mid && f[t1].wz<=f[t2].wz)||t2>r) jl[i]=f[t1++];
      else jl[i]=f[t2++];

    for (int i=l;i<=r;i++)
      if ((f[i]=jl[i]).tim<=mid) add(f[i].z,1),all++;
      else f[i].ans+=all-qurry(f[i].z);
    for (int i=l;i<=r;i++) if (f[i].tim<=mid) add(f[i].z,-1);

    for (int i=r;i>=l;i--)
      if (f[i].tim<=mid) add(f[i].z,1);
      else f[i].ans+=qurry(f[i].z);
    for (int i=l;i<=r;i++) if (f[i].tim<=mid) add(f[i].z,-1);
}
int cmp(hh a,hh b){return a.tim<b.tim;}
int main()
{
    scanf("%d%d",&n,&m);
    int x,tem=0;
    for(int i=1;i<=n;i++)
        scanf("%d",&x),a[x]=i;
    for(int i=1;i<=m;i++)
        scanf("%d",&in[i]),ok[in[i]]=1;
    for(int i=1;i<=n;i++)
        if(!ok[i]) f[++tem].z=i,f[tem].wz=a[f[tem].z],f[tem].tim=tem;
    for(int i=m;i;i--)
        f[++tem].z=in[i],f[tem].wz=a[f[tem].z],f[tem].tim=tem;
    cdq(1,n);
    sort(f+1,f+n+1,cmp);
    for (int i=2;i<=n;i++)
      f[i].ans+=f[i-1].ans;
    for (int i=n;i>n-m;i--) printf("%lld\n",f[i].ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值