您的位置:首页 > 其它

bzoj 3295: [Cqoi2011]动态逆序对 cdq分治+树状数组

2016-10-24 19:30 513 查看

题目大意

给出1-n的一个排列和m个操作,每个操作有一个数字x表示在序列中把x这个数删掉,并且求在删掉x前该序列中有多少个逆序对。

n<=100000,m<=50000

分析

这题看完题后就想到了可以用树套树来搞,但是比较麻烦(其实也不是很麻烦)并且对于n<=100000,m<=50000的数据范围如果用nlog^2的复杂度去做的话会跑的很慢,于是就选择了新学的cdq分治。

先一开始往序列里面插入没被删除的数,然后从后往前的顺序处理操作。把每个操作拆分成两个操作,一个是插入,一个是询问,顺序不限。

那么问题就转换成了对于三元组(x,y,z)的处理,x是操作序号,y是下标,z是具体数值。对于这种问题一般的处理方式都是第一维直接排序,第二维cdq分治掉,第三维树状数组。

具体的就跟bzoj 3262一样啦。

注意要开long long

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 100005
#define ll long long
using namespace std;

int c
,a
,pos
,n,m,cnt,tot,vis
,num
,ans
;
struct data{int x,pos,op,id;}q[N*10],t[N*10];

void ins(int x,int y)
{
while (x<=n)
{
c[x]+=y;
x+=x&(-x);
}
}

int query(int x)
{
int ans=0;
while (x)
{
ans+=c[x];
x-=x&(-x);
}
return ans;
}

void cdq(int l,int r)
{
if (l>=r) return;
int mid=(l+r)/2;
cdq(l,mid);cdq(mid+1,r);
for (int j=mid+1,i=l;j<=r;j++)
{
while (q[i].pos<q[j].pos&&i<=mid)
{
if (q[i].op==1) ins(q[i].x,1);
i++;
}
if (q[j].op==2) ans[q[j].id]+=query(q[j].x-1);
}
for (int i=l,j=mid+1;j<=r;j++)
while (q[i].pos<q[j].pos&&i<=mid)
{
if (q[i].op==1) ins(q[i].x,-1);
i++;
}
for (int i=mid,j=r;j>mid;j--)
{
while (q[i].pos>q[j].pos&&i>=l)
{
if (q[i].op==1) ins(q[i].x,1);
i--;
}
if (q[j].op==2) ans[q[j].id]+=query(n)-query(q[j].x);
}
for (int i=mid,j=r;j>mid;j--)
while (q[i].pos>q[j].pos&&i>=l)
{
if (q[i].op==1) ins(q[i].x,-1);
i--;
}
int i=l,j=mid+1,k=l;
while (i<=mid||j<=r)
if (q[i].pos<q[j].pos&&i<=mid||j>r) t[k++]=q[i++];
else t[k++]=q[j++];
for (int i=l;i<=r;i++)
q[i]=t[i];
}

int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[a[i]]=i;
}
for (int i=1;i<=m;i++)
{
scanf("%d",&num[i]);
vis[pos[num[i]]]=1;
}
ll sum=0;
for (int i=1;i<=n;i++)
{
sum+=query(n)-query(a[i]);
ins(a[i],1);
}
for (int i=1;i<=n;i++)
ins(a[i],-1);
for (int i=1;i<=n;i++)
if (!vis[i])
{
q[++tot].op=1;
q[tot].pos=i;
q[tot].x=n-a[i]+1;
}
for (int i=m;i>=1;i--)
{
q[++tot].op=1;
q[tot].pos=pos[num[i]];
q[tot].x=n-num[i]+1;
q[++tot].op=2;
q[tot].pos=pos[num[i]];
q[tot].x=n-num[i]+1;
q[tot].id=++cnt;
}
cdq(1,tot);
for (int i=cnt;i>=1;i--)
{
printf("%lld\n",sum);
sum-=ans[i];
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: