CQOI2011动态逆序对--cdq分治

来源:互联网 发布:谷歌编程面试问题 编辑:程序博客网 时间:2024/05/18 00:45

题意:给出一个1~n的排列,按顺序删掉m个元素,求出每次删除操作之前序列的逆序对个数.

做法:这题显然可以用树套树之类的工业结构维护,但是我怎么可能会呢.

考虑离线cdq分治,可以把问题反过来,转化为每次添加一个元素,求添加后序列的逆序对个数.

把每个元素看成平面上的点,x,y坐标分别是元素大小和插入位置,产生贡献的一对点则满足(x<x')!=(y<y').

设f[i]是添加第i个元素后,包含i的逆序对个数,那么只有之前添加的点对它产生影响,x维排序,y维用树状数组维护,然后x,y交换再做一遍就好了.

PS:之前排序用的sort,就T了,简直太迷了.

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define REP(I,ST,ED) for(int I=ST,I##end=ED;I<=I##end;++I)#define DREP(I,ST,ED) for(int I=ST,I##end=ED;I>=I##end;--I)typedef long long ll;namespace ioput{int read(){int x=0;char c=getchar();while((c<'0')||(c>'9'))c=getchar();while((c>='0')&&(c<='9'))x=x*10+c-'0',c=getchar();return x;}char t[20];int tlen;void write(ll x){t[tlen=0]='\n';if(x==0)t[++tlen]=0;while(x)t[++tlen]=x%10+'0',x/=10;DREP(i,tlen,0)putchar(t[i]);}}using namespace ioput;const int maxn=100005;struct node{int x,y,id;}a[maxn],b[maxn],ta[maxn];int n,m,tot,pos[maxn];bool vis[maxn];ll ans[maxn];namespace Binary_Indexed_Tree{ll c[maxn];void add(int x,int val){while(x<=n)c[x]+=val,x+=x&-x;}int query(int x){int res=0;while(x)res+=c[x],x-=x&-x;return res;}}using namespace Binary_Indexed_Tree;void solve(int l,int r){if(l>=r)return;int mid=(l+r)>>1,cur=l,curl=l,curr=mid+1;solve(l,mid),solve(mid+1,r);while((curl<=mid)&&(curr<=r)){if(a[curl].x<a[curr].x)add(a[curl].y,1),b[cur++]=a[curl++];else ans[a[curr].id]+=query(n)-query(a[curr].y),b[cur++]=a[curr++];}REP(i,curl,mid)b[cur++]=a[i];REP(i,curr,r)ans[a[i].id]+=query(n)-query(a[i].y),b[cur++]=a[i];REP(i,l,curl-1)add(a[i].y,-1);REP(i,l,r)a[i]=b[i];}int main(){#ifndef ONLINE_JUDGEfreopen("in.txt","r",stdin);freopen("out.txt","w",stdout);#endifint u,tmp=0;n=read(),m=read();REP(i,1,n)pos[read()]=i;DREP(i,n,n-m+1){vis[u=read()]=1;a[i]=(node){u,pos[u],i};}REP(i,1,n)if(!vis[i])a[++tmp]=(node){i,pos[i],tmp};REP(i,1,n)ta[i]=a[i];solve(1,n);REP(i,1,n)a[i]=ta[i],swap(a[i].x,a[i].y);solve(1,n);REP(i,1,n)ans[i]+=ans[i-1];DREP(i,n,n-m+1)write(ans[i]);return 0;}


阅读全文
1 0
原创粉丝点击