BZOJ3295: [Cqoi2011]动态逆序对
Description
对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。
给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数
Input
输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。
以下n行每行包含一个1到n之间的正整数,即初始排列。
以下m行每行一个正整数,依次为每次删除的元素。
N<=100000 M<=50000
Output
输出包含m行,依次为删除每个元素之前,逆序对的个数。
Sample Input
5 4
1
5
3
4
2
5
1
4
2
1
5
3
4
2
5
1
4
2
Sample Output
5
2
2
1
样例解释
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。
2
2
1
样例解释
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。
题解Here!
据说正解是cdq分治?本蒟蒻并不会。。。
于是想起了这题:P1774 最接近神的人_NOI导刊2010提高(02)
那题是静态的逆序对,而这题要支持删除操作。
静态逆序对用了什么?树状数组!
树状数组维护什么?前缀和!
动态前缀和怎么维护?主席树!
于是一个树状数组套主席树就这么被YY构造出来了。。。
删除第一个前的逆序对自然能求出来.
此时我们考虑删除第一个元素,减少的逆序对个数就是在它前面的比他大的和在它后面比它小的,于是我们开两个数组来存:
sum_one[i] 表示的就是在i前面而且大于i位置上的元素的元素的个数。
sum_two[i] 表示的就是在i后面而且小于i位置上的元素的元素的个数。
sum_one[i] 就可以在读入的时候直接处理一下。
sum_two[i] 可以反向添加然后处理出来。
但是又有一个问题了,我已经删了的还是被计算在内了,怎么办?
我减去它与已经删了的构成的逆序对的个数不就好了吗?
于是问题变成了:
每一次在已经删除的元素里面找 [l,r] 里面小于某个数的元素的个数。
这个问题显然丢给主席树。
注:记得开大空间!我就是数组开小了然后 MLE* 2 。。。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define MAXN 100010 using namespace std; int n,m; long long ans=0; int val[MAXN],pos[MAXN],sum_one[MAXN],sum_two[MAXN]; inline int read(){ int date=0,w=1;char c=0,last=0; while(c<'0'||c>'9'){last=c;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} if(last=='-')w=-1; return date*w; } namespace CT{ int size=1,s1,s2,root[MAXN],lrt[MAXN/1000+10],rrt[MAXN/1000+10]; struct Charman_Tree{ int l,r,sum; }a[MAXN*100]; void insert(int k,int l,int r,int &rt){ a[size]=a[rt];rt=size++; a[rt].sum++; if(l==r)return; int mid=l+r>>1; if(k<=mid)insert(k,l,mid,a[rt].l); else insert(k,mid+1,r,a[rt].r); } int query_front(int l,int r,int k){ if(l==r)return 0; int mid=l+r>>1,t=0; for(int i=1;i<=s1;i++)t-=a[a[lrt[i]].r].sum; for(int i=1;i<=s2;i++)t+=a[a[rrt[i]].r].sum; if(k<=mid){ for(int i=1;i<=s1;i++)lrt[i]=a[lrt[i]].l; for(int i=1;i<=s2;i++)rrt[i]=a[rrt[i]].l; return t+query_front(l,mid,k); } else{ for(int i=1;i<=s1;i++)lrt[i]=a[lrt[i]].r; for(int i=1;i<=s2;i++)rrt[i]=a[rrt[i]].r; return query_front(mid+1,r,k); } } int query_next(int l,int r,int k){ if(l==r)return 0; int mid=l+r>>1,t=0; for(int i=1;i<=s1;i++)t-=a[a[lrt[i]].l].sum; for(int i=1;i<=s2;i++)t+=a[a[rrt[i]].l].sum; if(k>mid){ for(int i=1;i<=s1;i++)lrt[i]=a[lrt[i]].r; for(int i=1;i<=s2;i++)rrt[i]=a[rrt[i]].r; return t+query_next(mid+1,r,k); } else{ for(int i=1;i<=s1;i++)lrt[i]=a[lrt[i]].l; for(int i=1;i<=s2;i++)rrt[i]=a[rrt[i]].l; return query_next(l,mid,k); } } inline int lowbit(int x){return x&(-x);} inline void update(int x,int v){ for(int i=x;i<=n;i+=lowbit(i))insert(v,1,n,root[i]); } inline int get_answer(int l,int r,int k){ int s=0; s1=s2=0; for(int i=0;i;i-=lowbit(i))lrt[++s1]=root[i]; for(int i=l;i;i-=lowbit(i))rrt[++s2]=root[i]; s+=query_front(1,n,k); s1=s2=0; for(int i=r-1;i;i-=lowbit(i))lrt[++s1]=root[i]; for(int i=n;i;i-=lowbit(i))rrt[++s2]=root[i]; s+=query_next(1,n,k); return s; } } namespace BIT{ int tree[MAXN]; inline void init(){memset(tree,0,sizeof(tree));} inline int lowbit(int x){return x&(-x);} inline void update(int x,int v){for(;x<=n;x+=lowbit(x))tree[x]+=v;} inline int sum(int x){int s=0;for(;x;x-=lowbit(x))s+=tree[x];return s;} } void work(){ int x,id; while(m--){ printf("%lld\n",ans); x=read();x=pos[x]; ans-=(sum_one[x]+sum_two[x]-CT::get_answer(x-1,x+1,val[x])); CT::update(x,val[x]); } } void init(){ n=read();m=read(); for(int i=1;i<=n;i++){ val[i]=read(); pos[val[i]]=i; sum_one[i]=BIT::sum(n)-BIT::sum(val[i]); ans+=(long long)sum_one[i]; BIT::update(val[i],1); } BIT::init(); for(int i=n;i>=1;i--){ sum_two[i]=BIT::sum(val[i]-1); BIT::update(val[i],1); } } int main(){ init(); work(); return 0; }