HYSBZ - 3295  动态逆序对 (cdq分治)

博客介绍了HYSBZ 3295题目的解决方案,即动态逆序对问题。通过将删除操作逆向思考为插入,将问题转化为三维偏序问题,利用CDQ分治策略,重点在于在第二维上进行归并,提高了效率。
摘要由CSDN通过智能技术生成

F - 动态逆序对

 HYSBZ - 3295 

题目大意:给出一个序列有m个询问,每一个询问要求输出当前的逆序对数量后在原序列中删除该数字。

解题思路:如果将删除倒着做就变成了插入。没插入一个数字我们就统计当前数字对逆序对产生的贡献就是

这里有三维 插入的时间t  数字的值v 和 插入的位置pos

所以就转化为了三维偏序问题。

第一维是时间,第二维就是值 第三维是pos

对于每个查询我们就统计插入时间小于它的 位置小于它的值大于它的和位置大于他值小于它的

然后将查询做一个前缀和。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
#define sca(x) scanf("%d",&x)
#define lowb(x) (x&(-x))
#define pb(x) push_back(x)
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define per(i,j,k) for(int i=j;i>=k;i--)
#define pri(x) printf("%d\n",x);
#define N 100005
#define inf 0x3f3f3f3f
const LL mod=1e9+7;


struct node
{
    int val,pos,qid;
}q[N*2];

int a[N],b[N],vis[N],vis1[N];
LL ou[N];

bool cmpy(node a,node b)
{
    return a.val>b.val;
}

struct BIT
{
    int c[N];
    int maxn;
    void init(int n)
    {
        maxn=n;
    }
    void add(int x,int val,int t)
    {
        if(t){
            for(int i=x;i<=maxn;i+=lowb(i))c[i]+=val;
        }
        else{
            for(int i=x;i;i-=lowb(i))c[i]+=val;
        }
    }

    int ask(int x,int t)
    {
        int ans=0;
        if(t){
            for(int i=x;i;i-=lowb(i))ans+=c[i];
        }
        else{
            for(int i=x;i<=maxn;i+=lowb(i))ans+=c[i];
        }
        return ans;
    }
}bit;
void cdq(int l,int r)
{
    if(l==r) return ;
    int m=(l+r)>>1;
    cdq(l,m);cdq(m+1,r);
    sort(q+l,q+m+1,cmpy);
    sort(q+m+1,q+r+1,cmpy);
    int L=l,R=m+1;
    for(;R<=r;R++)
    {
        while(q[L].val>q[R].val&&L<=m)
        {
            bit.add(q[L].pos,1,1);
            L++;
        }
        ou[q[R].qid]+=bit.ask(q[R].pos,1);
    }
    for(int i=l;i<L;i++)bit.add(q[i].pos,-1,1);
    R=r,L=m;
    for(;R>m;R--)
    {
        while(q[L].val<q[R].val&&L>=l)
        {
            bit.add(q[L].pos,1,0);
            L--;
        }
        ou[q[R].qid]+=bit.ask(q[R].pos,0);
    }
    for(int i=m;i>L;i--)bit.add(q[i].pos,-1,0);
}
int main()
{
    int n,m;
    cin>>n>>m;
    bit.init(n);
    rep(i,1,n)sca(a[i]),vis1[a[i]]=i;
    rep(i,1,m)sca(b[i]),vis[b[i]]=1;
    int tot=1;
    int qid=1;
    rep(i,1,n)
    {
        if(!vis[a[i]])q[tot++]=node{a[i],vis1[a[i]],qid++};
    }
    per(i,m,1)
    {
        q[tot++]=node{b[i],vis1[b[i]],qid++};
    }
    cdq(1,tot-1);
    rep(i,2,qid-1)ou[i]+=ou[i-1];
    per(i,qid-1,qid-m)printf("%lld\n",ou[i]);
}

虽然cdq分治还是很快的。但是cdq分治里边套sort感觉不太好,其实我们在cdq的过程中对第二维进行归并就可以了。

这样比原来大概快了一半。。。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
#define sca(x) scanf("%d",&x)
#define lowb(x) (x&(-x))
#define pb(x) push_back(x)
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define per(i,j,k) for(int i=j;i>=k;i--)
#define pri(x) printf("%d\n",x);
#define N 100005
#define inf 0x3f3f3f3f
const LL mod=1e9+7;


struct node
{
    int val,pos,qid;
}q[N*2],q1[N];

int a[N],b[N],vis[N],vis1[N];
LL ou[N];

bool cmpy(node a,node b)
{
    return a.val>b.val;
}

struct BIT
{
    int c[N];
    int maxn;
    void init(int n)
    {
        maxn=n;
    }
    void add(int x,int val,int t)
    {
        if(t){
            for(int i=x;i<=maxn;i+=lowb(i))c[i]+=val;
        }
        else{
            for(int i=x;i;i-=lowb(i))c[i]+=val;
        }
    }

    int ask(int x,int t)
    {
        int ans=0;
        if(t){
            for(int i=x;i;i-=lowb(i))ans+=c[i];
        }
        else{
            for(int i=x;i<=maxn;i+=lowb(i))ans+=c[i];
        }
        return ans;
    }
}bit;
void cdq(int l,int r)
{
    if(l==r) return ;
    int m=(l+r)>>1;
    cdq(l,m);cdq(m+1,r);
    int now=l;
    int L=l,R=m+1;
    for(;R<=r;R++)
    {
        while(q[L].val>q[R].val&&L<=m)
        {
            bit.add(q[L].pos,1,1);
            q1[now++]=q[L];
            L++;
        }
        ou[q[R].qid]+=bit.ask(q[R].pos,1);
        q1[now++]=q[R];
    }
    for(int i=L;i<=m;i++)q1[now++]=q[i];
    for(int i=l;i<L;i++)bit.add(q[i].pos,-1,1);
    R=r,L=m;
    for(;R>m;R--)
    {
        while(q[L].val<q[R].val&&L>=l)
        {
            bit.add(q[L].pos,1,0);
            L--;
        }
        ou[q[R].qid]+=bit.ask(q[R].pos,0);
    }
    for(int i=m;i>L;i--)bit.add(q[i].pos,-1,0);
    for(int i=l;i<=r;i++)q[i]=q1[i];
}
int main()
{
    int n,m;
    cin>>n>>m;
    bit.init(n);
    rep(i,1,n)sca(a[i]),vis1[a[i]]=i;
    rep(i,1,m)sca(b[i]),vis[b[i]]=1;
    int tot=1;
    int qid=1;
    rep(i,1,n)
    {
        if(!vis[a[i]])q[tot++]=node{a[i],vis1[a[i]],qid++};
    }
    per(i,m,1)
    {
        q[tot++]=node{b[i],vis1[b[i]],qid++};
    }
    cdq(1,tot-1);
    rep(i,2,qid-1)ou[i]+=ou[i-1];
    per(i,qid-1,qid-m)printf("%lld\n",ou[i]);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值