【BZOJ】3295: [Cqoi2011]动态逆序对

本文介绍了一种结合CDQ分治与树状数组的数据结构算法,用于解决给定序列上的逆序对计数问题。适用于n<=10^5,操作次数m<=50000的大数据规模问题,通过两次CDQ分治分别统计右下偏序与左上偏序中的逆序对数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题意】给定1~n的排列,m次操作每次删除一个数,求每次操作前的逆序对数。n<=10^5,m<=50000。

【算法】CDQ分治+树状数组

【题解】先树状数组求逆序对,然后统计每次删除的逆序对数。

每个数字及其坐标构成一个二维坐标,逆序对就是两个相对位置在左上右下的点,那么删除一个点会减少的逆序对就是其左上偏序和右下偏序中的点数,转化为有操作顺序的二维平面矩阵查询和单点修改,用CDQ分治或树套树解决。

具体细节:

1.先加入所有加点操作(原数字必须作为最开始的操作进入CDQ分治过程),再加入前m-1个删除和询问。

2.第一次统计右下偏序,按x轴逆序排序后CDQ分治。

3.第二次统计左上偏序,令所有点y=n-y+1,然后按x轴正序排序后CDQ分治。

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define lowbit(x) (x&-x)
using namespace std;
int read(){
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c=='-')t=-1;
    do{s=s*10+c-'0';}while(isdigit(c=getchar()));
    return s*t;
}
const int maxn=100010;
int n,m,ans[maxn],c[maxn],t[maxn],A[maxn],tot=0;
struct cyc{int id,kind,qid,x,y;}a[maxn*2],b[maxn*2];
long long ANS=0;
void modify(int x,int k){for(int i=x;i<=n;i+=lowbit(i))c[i]+=k;}
int query(int x){int as=0;for(int i=x;i>=1;i-=lowbit(i))as+=c[i];return as;}
bool cmp(cyc a,cyc b){return a.x<b.x||(a.x==b.x&&a.id<b.id);}
bool cmp2(cyc a,cyc b){return a.x>b.x||(a.x==b.x&&a.id<b.id);}
void CDQ(int l,int r){
    if(l==r)return;
    int mid=(l+r)>>1;
    for(int i=l;i<=r;i++){
        if(a[i].id<=mid&&!a[i].kind)modify(a[i].y,a[i].qid?-1:1);
        if(a[i].id>mid&&a[i].kind)ans[a[i].qid]+=query(a[i].y);
    }
    for(int i=l;i<=r;i++)if(a[i].id<=mid&&!a[i].kind)modify(a[i].y,a[i].qid?1:-1);
    int x1=l-1,x2=mid;
    for(int i=l;i<=r;i++)if(a[i].id<=mid)b[++x1]=a[i];else b[++x2]=a[i];
    for(int i=l;i<=r;i++)a[i]=b[i];
    CDQ(l,mid);CDQ(mid+1,r);
}    
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        A[i]=read();
        t[A[i]]=i;
        modify(A[i],1);
        ANS+=i-query(A[i]);
    }
    if(m>1)printf("%lld\n",ANS);
    memset(c,0,sizeof(c));
    for(int i=1;i<=n;i++)a[++tot]=(cyc){tot,0,0,i,A[i]};
    for(int i=1;i<m;i++){
        int x=read();
        a[++tot]=(cyc){tot,0,i,t[x],x};
        a[++tot]=(cyc){tot,1,i,t[x],x};
    }
    sort(a+1,a+tot+1,cmp2);
    CDQ(1,tot);
    for(int i=1;i<=tot;i++)a[i].y=n-a[i].y+1;
    sort(a+1,a+tot+1,cmp);
    CDQ(1,tot);
    for(int i=1;i<m;i++){
        ANS-=ans[i];
        printf("%lld\n",ANS);
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/onioncyc/p/8215263.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值