给一个1~n的排列A,要求按照某种顺序删除一些数(其他数顺序不变),输出每次删除之前逆序对的数目。所谓逆序对数,就是满足i<j且A[i]>A[j]的有序对(i,j)的数目
首先用O(nlogn)时间求出初始排列的逆序对数,则每次需要求出被删除的那个数左边有多少个数比它大,右边有多少个数比它小。
TLE的代码,忧伤。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define REP(i,n) for(int i=0;i<(n);++i)
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define FORD(i,a,b) for(int i=(a);i<=(b);++i)
const int maxn=200010;
using namespace std;
int n,m;
int arr[maxn],pos[maxn];
int res;
struct Node{
Node* ch[2];
int v,r,s;
};
Node* null=new Node();
struct Treap{
Node* root;
void init(){root=null;}
void push_up(Node* rt){
rt->s=rt->ch[0]->s+rt->ch[1]->s+1;
}
void rotate(Node* &o,int d){
Node* k=o->ch[d^1];
o->ch[d^1]=k->ch[d];
k->ch[d]=o;
push_up(o);
push_up(k);
o=k;
}
void insert(Node* &o,int x){
if(o==null){
o=new Node();
o->ch[0]=o->ch[1]=null;
o->s=1;
o->r=rand();
o->v=x;
}else{
int d=(o->v>x?0:1);
insert(o->ch[d],x);
if(o->ch[d]->r>o->r) rotate(o,d^1);
}
push_up(o);
}
void remove(Node* &o,int x){
if(o==null) return;
int d=(o->v>x?0:1);
if(o->v==x){
Node* u=o;
if(o->ch[0]!=null&o->ch[1]!=null){
int d2=(o->ch[0]->r>o->ch[1]->r?1:0);
rotate(o,d2);remove(o->ch[d],x);
}else{
if(o->ch[0]==null) o=o->ch[1];
else o=o->ch[0];
delete(u);
}
}else remove(o->ch[d],x);
if(o!=null) push_up(o);
}
int findk(Node* o,int x){
if(o==null) return 0;
if(o->v==x) return o->ch[0]->s+1;
else if(o->v>x) return findk(o->ch[0],x);
else return o->ch[0]->s+1+findk(o->ch[1],x);
}
};
struct node{
Treap tree;
void init(){tree.init();}
int get(int x){
return tree.findk(tree.root,x);
}
void update(int x){
if(x>0) tree.insert(tree.root,x);
else tree.remove(tree.root,-x);
}
}s[maxn];
struct IndexTree{
int c[maxn];
void init(){memset(c,0,sizeof(c));}
void update(int x,int v){
for(int i=x;i<=n;i+=i&-i) c[i]+=v;
}
int get_sum(int x){
int sum=0;
for(int i=x;i>0;i-=i&-i) sum+=c[i];
return sum;
}
}all,now;
void inc(int p,int v){
for(int i=p;i<=n;i+=i&-i) s[i].update(v);
}
int query(int x,int v){
int ans=0;
for(int i=x;i>0;i-=i&-i){
ans+=s[i].get(v);
}
return ans;
}
void init(){
res=0;
all.init();
now.init();
FORD(i,1,n) s[i].init();
}
void build(){
init();
FORD(i,1,n){
scanf("%d",&arr[i]);
pos[arr[i]]=i;
res+=i-all.get_sum(arr[i])-1;
all.update(arr[i],1);
now.update(i,1);
inc(i,arr[i]);
}
}
void solve(int x){
int k=now.get_sum(pos[x]-1);
int a=all.get_sum(x-1);
int b=query(pos[x],x-1);
all.update(x,-1);
now.update(pos[x],-1);
inc(pos[x],-x);
// cout<<"a="<<a<<" b="<<b<<" k="<<k<<endl;
// cout<<"disinc="<<k-b+a-b<<endl;
res=res-(k-b+a-b);
}
int main()
{
//#ifndef ONLINE_JUDGE
//freopen("in.cpp","r",stdin);
//#endif // ONLINE_JUDGE
while(scanf("%d%d",&n,&m)!=EOF){
build();
int x;
while(m--){
printf("==%d\n",res);
scanf("%d",&x);
solve(x);
}
}
return 0;
}
本文介绍了一种针对排列A的逆序对计数算法,该算法能够在删除元素的同时高效地更新逆序对的数量。使用了Treap数据结构来维护元素的位置信息,并通过线段树辅助实现快速查询。
672

被折叠的 条评论
为什么被折叠?



