BZOJ3295: [Cqoi2011]动态逆序对

BZOJ3295

删除好像挺麻烦的。。那就考虑倒一下,也就是倒序加值。
考虑一个值加入进来,对答案的影响就是,它左边的比它大的个数和它右边比它小的个数加和。
先只考虑一半。只考虑右边比它小的数的个数。那么假设当前加入的为一个三元组
(t,pos,x) ,表示时间,位置和值。那么就是求满足 tt<tppos>posxx<x 的三元组 (tt,ppos,xx) 的个数。(因为把删除操作反过来进行了所以就是 tt<t )。
那么三维偏序就可以考虑用 CDQ 分治。
当然这只完成了一半啦,剩下的一半其实也相同。
我偷了点懒。。想只写一遍 CDQ 。另外一半操作的时候,把位置和值都颠倒了一下。也就是原本应该插入的是 15342 ,变成了 42315 。那么相当于每次也是求右边的比它小的个数啦。
对应到代码中:第一次操作是

for(int i=1;i<=n;i++) if(!flag[i]) Q[++tot]=Query(0,Pos[i],i,0);
    for(int i=m;i;i--) {
        Q[++tot]=Query(1,Pos[b[i]],b[i],m-i+1);
        Q[++tot]=Query(0,Pos[b[i]],b[i],m-i+1);
    }

后面一遍操作写成:

for(int i=1;i<=n;i++) if(!flag[i]) Q[++tot]=Query(0,n-Pos[i]+1,n-i+1,0);
    for(int i=m;i;i--) {
        Q[++tot]=Query(1,n-Pos[b[i]]+1,n-b[i]+1,m-i+1);
        Q[++tot]=Query(0,n-Pos[b[i]]+1,n-b[i]+1,m-i+1);
    }

Warning!
相同的位置应先查询再修改。
要开 longlong .
DontForget 要求出删完的序列中逆序对的个数!

【代码】

#include <cstdio>
#include  <iostream>
#include <algorithm>
#define N 100005
#define M 200005
using namespace std;
typedef long long ll;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

int n,m,tot;
int a[N],b[N],Pos[N];
ll szsz[N],ans[N],PreAns;
bool flag[N];

class Query{
    public:
    int type,pos,x,id;
    Query(){}
    Query(int tt,int pp,int xx,int ii){
        type=tt,pos=pp,x=xx,id=ii;
    }
}Q[M],tmp[M];

bool operator <(Query a,Query b){
    return a.pos>b.pos||(a.pos==b.pos&&a.type>b.type);
}

int lowbit(int x){return x&-x;}

void Sum_Up(int x,int y){
    for(int i=x;i<=n;i+=lowbit(i)) szsz[i]+=y;
}

ll query(int x){
    ll rtn=0;
    for(int i=x;i;i-=lowbit(i)) rtn+=szsz[i];
    return rtn;
}

void Clear(int x){
    for(int i=x;i<=n&&szsz[i];i+=lowbit(i)) szsz[i]=0;
}

void Input_Init()
{
    n=read();m=read();
    for(int i=1;i<=n;i++) Pos[a[i]=read()]=i;
    for(int i=1;i<=m;i++) flag[b[i]=read()]=1;
    for(int i=1;i<=n;i++) if(!flag[i]) Q[++tot]=Query(0,Pos[i],i,0);
    for(int i=m;i;i--) {
        Q[++tot]=Query(1,Pos[b[i]],b[i],m-i+1);
        Q[++tot]=Query(0,Pos[b[i]],b[i],m-i+1);
    }
}

int st[N],top;
void Get_PreAns()
{
    for(int i=1;i<=n;i++) if(!flag[a[i]]) st[++top]=a[i];
    for(int i=1;i<=top;i++) 
    {
        PreAns+=i-1-query(st[i]);
        Sum_Up(st[i],1);
    }
    for(int i=1;i<=top;i++) Clear(st[i]);
}

bool Judge(Query a,Query b){
    return a.pos<b.pos||(a.pos==b.pos&&a.type>b.type);
}

void CDQ(int L,int R)
{
    if(L==R) return;
    int mid=L+R>>1;CDQ(L,mid);CDQ(mid+1,R);
    int p=L,q=mid+1,o=0;
    while(p<=mid&&q<=R) {
        if(Q[p]<Q[q]) {
            if(!Q[p].type) Sum_Up(Q[p].x,1);
            tmp[o++]=Q[p++];
        }
        else {
            if(Q[q].type) ans[Q[q].id]+=query(Q[q].x);
            tmp[o++]=Q[q++];
        }
    }
    while(p<=mid) tmp[o++]=Q[p++];
    while(q<=R) {
        if(Q[q].type) ans[Q[q].id]+=query(Q[q].x);
        tmp[o++]=Q[q++];
    }
    for(int i=0;i<o;i++) {
        Q[i+L]=tmp[i];
        Clear(tmp[i].x);
    }
}

void Re_Make()
{
    tot=0;
    for(int i=1;i<=n;i++) if(!flag[i]) Q[++tot]=Query(0,n-Pos[i]+1,n-i+1,0);
    for(int i=m;i;i--) {
        Q[++tot]=Query(1,n-Pos[b[i]]+1,n-b[i]+1,m-i+1);
        Q[++tot]=Query(0,n-Pos[b[i]]+1,n-b[i]+1,m-i+1);
    }
}

void Solve()
{
    CDQ(1,tot);
    Re_Make();CDQ(1,tot);ans[0]=PreAns;
    for(int i=1;i<=m;i++) ans[i]=ans[i-1]+ans[i];
    for(int i=m;i;i--) printf("%lld\n",ans[i]);
}

int main()
{
    Input_Init();
    Get_PreAns();
    Solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值