题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3295
题目分析:这题裸的树套树啊。我们先算出原序列的逆序对数量,当一个数被删除的时候,逆序对的减小量=此时在它前面的比它大的数的个数+此时在它后面的比它小的数的个数。于是我们用一个树状数组套平衡树就可以搞定了。
CODE(BIT+treap):
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=100100;
const int lg=20;
const long long M=1000000009;
const long long Times=998244353;
typedef long long LL;
struct Tnode
{
int val,fix,Size;
Tnode *lson,*rson;
int Lsize() { return (lson? lson->Size:0); }
int Rsize() { return (rson? rson->Size:0); }
void Get_size() { Size=Lsize()+Rsize()+1; }
} tree[maxn*lg];
Tnode *bit[maxn];
int cur=-1;
LL seed,ans=0;
int a[maxn];
int p[maxn];
int n,m;
int Rand()
{
seed=seed*Times%M;
return (int)seed;
}
Tnode *New_node(int v)
{
cur++;
tree[cur].val=v;
tree[cur].fix=Rand();
tree[cur].Size=1;
tree[cur].lson=tree[cur].rson=NULL;
return tree+cur;
}
void Right_turn(Tnode *&P)
{
Tnode *W=P->lson;
P->lson=W->rson;
W->rson=P;
P=W;
P->rson->Get_size();
P->Get_size();
}
void Left_turn(Tnode *&P)
{
Tnode *W=P->rson;
P->rson=W->lson;
W->lson=P;
P=W;
P->lson->Get_size();
P->Get_size();
}
void Insert(Tnode *&P,int v)
{
if (!P) P=New_node(v);
else
{
if ( v<P->val )
{
Insert(P->lson,v);
if ( P->lson->fix<P->fix ) Right_turn(P);
}
else
{
Insert(P->rson,v);
if ( P->rson->fix<P->fix ) Left_turn(P);
}
P->Get_size();
}
}
void Add(int x,int v)
{
while (x<=n)
{
Insert(bit[x],v);
x+=(x&(-x));
}
}
int Query(Tnode *P,int v)
{
if (!P) return 0;
if ( P->val<v ) return P->Lsize()+1+Query(P->rson,v);
return Query(P->lson,v);
}
int Sum(int x,int v,bool f)
{
int sum=0;
while (x)
{
int temp=Query(bit[x],v);
if ( f && bit[x] ) temp=bit[x]->Size-temp;
sum+=temp;
x-=(x&(-x));
}
return sum;
}
void Delete(Tnode *&P,int v)
{
if ( P->val==v )
if ( !P->lson )
if ( !P->rson ) P=NULL;
else P=P->rson;
else
if ( !P->rson ) P=P->lson;
else
if ( P->lson->fix<P->rson->fix )
Right_turn(P),Delete(P->rson,v),P->Get_size();
else Left_turn(P),Delete(P->lson,v),P->Get_size();
else
if ( v<P->val ) Delete(P->lson,v),P->Get_size();
else Delete(P->rson,v),P->Get_size();
}
void Dec(int x,int v)
{
while (x<=n)
{
Delete(bit[x],v);
x+=(x&(-x));
}
}
int main()
{
freopen("3295.in","r",stdin);
freopen("3295.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++) scanf("%d",&a[i]),p[ a[i] ]=i,bit[i]=NULL;
seed=p[1];
for (int i=1; i<=n; i++) Add(i,a[i]),ans+=(long long)( Sum(i-1,a[i],1) );
for (int i=1; i<=m; i++)
{
printf("%lld\n",ans);
int x;
scanf("%d",&x);
Dec(p[x],x);
ans-=Sum(p[x]-1,x,1);
ans-=( Sum(n,x,0)-Sum(p[x],x,0) );
}
return 0;
}
当然,我们也可以用CDQ分治。如果我们将此题的操作序列抽象出来,大概就是这样:QDQDQDQD,其中Q表示查询,D表示删数。我们记nx[i]为逆序对中有i这个值的逆序对数,则原数列的逆序对总数为 sum=∑ni=1nx[i]2 。一开始令每一个查询答案都为sum,令每一个删除x的操作都会使逆序对数减少nx[x]。然后我们将操作序列分为两半,很明显左半部分的D会对右半部分的Q与D都产生影响,所以我们应先递归左半部分,知道每一个D实际上删除了多少个逆序对。然后将右边的Q的答案减去左边的D删除的逆序对数,并将左边的D和右边的D堆在一起排一次序,用树状数组记录左边Delete的数会对右边的D所删除的逆序对数产生怎样的影响。时间复杂度 O(nlog2(n)) 。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=100100;
const int maxm=maxn>>1;
typedef long long LL;
struct data
{
int id;
bool Left;
} b[maxn];
int work[maxn];
LL num[maxn];
int bit[maxn];
LL nx[maxn];
LL sum=0;
int a[maxn];
int p[maxn];
int n,m;
int Sum(int x)
{
int temp=0;
while (x)
{
temp+=bit[x];
x-=(x&(-x));
}
return temp;
}
void Add(int x,int v)
{
while (x<=n)
{
bit[x]+=v;
x+=(x&(-x));
}
}
bool Comp1(data x,data y)
{
return p[ work[ x.id ] ]<p[ work[ y.id ] ];
}
bool Comp2(data x,data y)
{
return p[ work[ x.id ] ]>p[ work[ y.id ] ];
}
void CDQ(int L,int R)
{
if (L==R) return;
int mid=(L+R)>>1;
CDQ(L,mid);
int temp=0;
LL dec=0;
for (int i=L; i<=mid; i++)
if (work[i])
{
temp++;
b[temp].id=i;
b[temp].Left=true;
dec+=num[i];
}
for (int i=mid+1; i<=R; i++)
if (work[i])
{
temp++;
b[temp].id=i;
b[temp].Left=false;
}
else num[i]-=dec;
sort(b+1,b+temp+1,Comp1);
int k=0;
for (int i=1; i<=temp; i++)
{
int j=b[i].id;
if (!b[i].Left) num[j]-=(k-Sum(work[j]));
else k++,Add(work[j],1);
}
for (int i=1; i<=temp; i++)
if (b[i].Left) Add(work[ b[i].id ],-1);
sort(b+1,b+temp+1,Comp2);
for (int i=1; i<=temp; i++)
{
int j=b[i].id;
if (!b[i].Left) num[j]-=Sum(work[j]-1);
else Add(work[j],1);
}
for (int i=1; i<=temp; i++)
if (b[i].Left) Add(work[ b[i].id ],-1);
CDQ(mid+1,R);
}
int main()
{
freopen("3295.in","r",stdin);
freopen("3295.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
p[ a[i] ]=i;
nx[i]=(i-1)-Sum(a[i]);
Add(a[i],1);
}
for (int i=1; i<=n; i++) bit[i]=0;
for (int i=n; i>=1; i--)
{
nx[i]+=Sum(a[i]-1);
Add(a[i],1);
}
for (int i=1; i<=n; i++) bit[i]=0,sum+=nx[i];
sum>>=1;
for (int i=1; i<=m; i++)
{
int x=(i<<1)-1;
num[x]=sum;
x++;
scanf("%d",&work[x]);
num[x]=nx[ p[ work[x] ] ];
}
CDQ(1,m<<1);
for (int i=1; i<(m<<1); i+=2) printf("%lld\n",num[i]);
return 0;
}