BZOJ3295: [Cqoi2011]动态逆序对(洛谷P3157)

292 篇文章 1 订阅
281 篇文章 1 订阅

CDQ分治

BZOJ题目传送门
洛谷题目传送门

我们可以把删除给反过来,变成从最终序列不断加入元素成为原始序列。

于是这道题就变成了元素下标分别为(时间、位置、值)的一个三维偏序问题( tj<ti,xj<xi,yj>yi t j < t i , x j < x i , y j > y i )。没有查询的元素 t t 随便给。

然后就变成CDQ分治裸题了。

先按照t排序,在区间内按照 x x 排,用树状数组维护y。统计时正着扫一遍计算大于 y y 的逆序对数,倒着扫一遍计算小于y的逆序对数。然后递归就行了。

注意要开 long long l o n g   l o n g

代码:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define F inline
using namespace std;
typedef long long LL;
struct px{ int x,y,t,z; }q[N],tem[N];
int n,m,i,t[N],ans[N],mp[N];
F char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
F int _read(){
    int x=0; char ch=readc();
    while (!isdigit(ch)) ch=readc();
    while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
    return x;
}
F void writec(LL x){ if (x>9) writec(x/10); putchar(x%10+48); }
F void _write(LL x){ writec(x),putchar('\n'); }
#define lbt(x) (x&-x)
F void nsrt(int x,int p){ while (x<=n) t[x]+=p,x+=lbt(x); }
F int srch(int x){ for (i=0;x;i+=t[x],x-=lbt(x)); return i; }
F bool cmp1(px a,px b){ return a.x==b.x?a.y<b.y:a.x<b.x; }
F bool cmp2(px a,px b){ return a.t<b.t; }
F void cdq(int l,int r){
    if (l==r) return; int mid=l+r>>1;
    for (int i=l;i<=mid;i++) tem[i]=q[i],tem[i].z=0;
    for (int i=mid+1;i<=r;i++) tem[i]=q[i],tem[i].z=1;
    sort(tem+l,tem+r+1,cmp1);
    for (int i=l;i<=r;i++)
        if (!tem[i].z) nsrt(tem[i].y,1); 
        else ans[tem[i].t]+=srch(n)-srch(tem[i].y);
    for (int i=l;i<=r;i++) if (!tem[i].z) nsrt(tem[i].y,-1);
    for (int i=r;i>=l;i--)
        if (!tem[i].z) nsrt(tem[i].y,1);
        else ans[tem[i].t]+=srch(tem[i].y);
    for (int i=l;i<=r;i++) if (!tem[i].z) nsrt(tem[i].y,-1);
    cdq(l,mid),cdq(mid+1,r);
}
int main(){
    n=_read(),m=_read(); int ti=n; LL sum=0;
    for (int i=1;i<=n;i++) q[i].x=i,mp[q[i].y=_read()]=i;
    for (int i=1;i<=m;i++) q[mp[_read()]].t=ti--;
    for (int i=1;i<=n;i++) if (!q[i].t) q[i].t=ti--;
    sort(q+1,q+n+1,cmp2),cdq(1,n);
    for (int i=1;i<=n;i++) sum+=ans[i];
    for (int i=n;i>n-m;i--) _write(sum),sum-=ans[i];
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值