您的位置:首页 > 其它

bzoj 3295 [Cqoi2011]动态逆序对(cdq分治,BIT)

2016-03-08 20:18 337 查看
【题目链接】

http://www.lydsy.com/JudgeOnline/problem.php?id=3295

【题意】

n个元素依次删除m个元素,求删除元素之前序列有多少个逆序对。

【思路】

cdq分治

这个题转化一下可以变成刚刚做过的三维偏序。

首先有两个量:序 和 值,可以将样例写成

x 1
2 3 4 5

y 1 5
3 4 2

然后因为我们要删除一些东西,所以加上时间,则样例变为

t 1 2
3 4 5

x 3 5
4 1 2

y 3 2
4 1 5

删除顺序就是按照t从大到小。我们把它看作t从小到大的插入结点。

则我们要求的是,一个结点在t时刻插入,左边有多少个比它大,右边有多少个比它小,设这个点为(t0,x0,y0),则我们要求的就是满足

t<t0,x<x0,y>y0

t<t0,x>x0,y<y0

的点数。因为具体的值对结果并无影响我们可以通过把a改成n-a+1来改变符号的方向,具体就是求满足

t<t0,x<x0,y<(n-y0+1)

t<t0,x<(n-x0+1),y<y0

的点数。

  于是问题变成了刚做过的
陌上花开 问题。

  最后统计每个时间点发生之前产生的所有逆序对。

【代码】

#include<cstdio>
#include<iostream>
#include<algorithm>
#define rep(a,b,c) for(int a=b;a<=c;++a)
using namespace std;

typedef long long ll;
const int N =3*1e5+10;

void read(int &x) {
char c=getchar(); x=0;
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
}

struct Node {
int a,b,c,ans;
bool operator<(const Node& rhs) const {
return a<rhs.a;
}
}q
,T
;
bool cmp(const Node& x,const Node& y)
{
return x.b<y.b||(x.b==y.b&&x.c>y.c);
}

int n,m,mx,C
,rk
;
ll ans
;

void add(int x,int v)
{
for(;x<=mx;x+=x&-x) C[x]+=v;
}
int query(int x)
{
int res=0;
for(;x;x-=x&-x) res+=C[x];
return res;
}

void solve(int l,int r)
{
if(l==r) return ;
int mid=(l+r)>>1;
solve(l,mid) , solve(mid+1,r);
int l1=l,l2=mid+1,i;
while(l2<=r) {
while(l1<=mid&&q[l1].b<q[l2].b) {
add(q[l1].c,1);
l1++;
}
q[l2].ans+=query(q[l2].c);
l2++;
}
for(i=l;i<l1;i++) add(q[i].c,-1);
l1=l,l2=mid+1; int now=l;
while(l1<=mid||l2<=r) {
if(l2>r||l1<=mid&&cmp(q[l1],q[l2])) T[now++]=q[l1++];
else T[now++]=q[l2++];
}
for(int i=l;i<=r;i++) q[i]=T[i];
}

int main()
{
read(n),read(m); mx=n+5;
rep(i,1,n) { q[i].b=i; read(q[i].c); }
int a;
rep(i,1,m) { read(a); rk[a]=i; }
int sz=m;
rep(i,1,n) if(!rk[i]) rk[i]=++sz;
rep(i,1,n) q[i].a=n-rk[q[i].c]+1;
sort(q+1,q+n+1);
rep(i,1,n) q[i].b=n-q[i].b+1;
solve(1,n);
sort(q+1,q+n+1);
rep(i,1,n) {
q[i].b=n-q[i].b+1;
q[i].c=n-q[i].c+1;
}
solve(1,n);
rep(i,1,n) ans[q[i].a]=q[i].ans;
rep(i,1,n) ans[i]+=ans[i-1];
for(int i=n;i>n-m;i--)
printf("%lld\n",ans[i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: