您的位置:首页 > 其它

【分块】【树套树】bzoj2141 排队

2014-09-12 15:21 369 查看
考虑暴力更新的情况,设swap的是L,R位置的数。
swap之后的逆序对数应该等于:
之前的逆序对数
+[L+1,R-1]中比 L位置的数 大的数的个数
-[L+1,R-1]中比 L位置的数 小的数的个数
-[L+1,R-1]中比 R位置的数 大的数的个数
+[L+1,R-1]中比 R位置的数 小的数的个数

并且若L位置的数>R位置的数,逆序对数--;反之,逆序对数++。

分块,对每个块内部进行sort,这样可以二分,在O(log(sqrt(n))的时间内求得某个块内比某个数大/小的数的个数。所以每次更新是O(sqrt(n)*log(sqrt(n))的。
若L和R所在的块相同或相邻时,直接暴力更新即可。

要用树状数组或归并排序求得初始答案。

要注意数据是可重的,所以要打时间戳。
所以为了lower_bound/upper_bound,要定义两套比较法则,一套双关键字,一套单关键字。

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct Point{int v,p;Point(const int &a,const int &b){v=a;p=b;}Point(){}};
bool operator < (const Point &a,const Point &b){return a.v<b.v;}//单关键字比较
bool operator > (const Point &a,const Point &b){return a.v>b.v;}
bool operator == (const Point &a,const Point &b){return a.v==b.v ? true : false;}
bool cmp(const Point &a,const Point &b){return a.v!=b.v ? a.v<b.v : a.p<b.p;}//双关键字比较
Point b[20001],a[20001],c[20001];
int n,sz,l[145],r[145],sum,num[20001],ans,x,y,m,mid,en;
int Res,Num;char C,CH[20];
inline int G()
{
Res=0;C='*';
while(C<'0'||C>'9')C=getchar();
while(C>='0'&&C<='9'){Res=Res*10+(C-'0');C=getchar();}
return Res;
}
inline void P(int x)
{
if(!x){putchar('0');putchar('\n');return;}
Num=0;while(x>0)CH[++Num]=x%10,x/=10;
while(Num)putchar(CH[Num--]+48);
putchar('\n');
}
void LiSan()
{
n=G();
for(int i=1;i<=n;i++){b[i].v=G();b[i].p=i;}
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;i++)
{
if(b[i].v!=b[i-1].v)en++;
a[b[i].p].v=en;
a[b[i].p].p=i;
}
}
//树状数组求出初始答案。
int D[20001];inline int lowbit(const int &x){return x&(-x);}
inline int getsum(int x){int res=0;while(x>0){res+=D[x];x-=lowbit(x);}return res;}
inline void add(int x,const int &d){while(x<=n){D[x]+=d;x+=lowbit(x);}}
void Get_First_Ans()
{for(int i=1;i<=n;i++){add(a[i].v,1);ans+=(i-getsum(a[i].v));}
P(ans);}
void makeblock()
{
sz=sqrt(n);
for(sum=1;sum*sz<n;sum++)
{
l[sum]=(sum-1)*sz+1;r[sum]=sum*sz;
for(int i=l[sum];i<=r[sum];i++)
num[i]=sum;
}
l[sum]=sz*(sum-1)+1;r[sum]=n;
for(int i=l[sum];i<=r[sum];i++)
num[i]=sum;
}
void Sort_Each_Block()
{for(int i=1;i<=n;i++)c[i]=a[i];
for(int i=1;i<=sum;i++)sort(a+l[i],a+r[i]+1,cmp);}
inline int Query(const int &L,const int &R)
{
swap( a[lower_bound(a+l[num[L]],a+r[num[L]]+1,c[L],cmp) - a ]
, a[lower_bound(a+l[num[R]],a+r[num[R]]+1,c[R],cmp)- a ]);//必须双关键字比较
sort(a+l[num[L]],a+r[num[L]]+1,cmp);
sort(a+l[num[R]],a+r[num[R]]+1,cmp);
int cnt=0;
if(c[L].v<c[R].v)cnt=1;
else if(c[L].v>c[R].v)cnt=-1;
swap(c[L],c[R]);
if(num[L]+1>=num[R])
for(int i=L+1;i<=R-1;i++)
{if(c[i].v>c[R].v)cnt++;else if(c[i].v<c[R].v)cnt--;
if(c[i].v>c[L].v)cnt--;else if(c[i].v<c[L].v)cnt++;}
else
{
for(int i=L+1;i<=r[num[L]];i++)
{if(c[i].v>c[R].v)cnt++;else if(c[i].v<c[R].v)cnt--;
if(c[i].v>c[L].v)cnt--;else if(c[i].v<c[L].v)cnt++;}
for(int i=l[num[R]];i<=R-1;i++)
{if(c[i].v>c[R].v)cnt++;else if(c[i].v<c[R].v)cnt--;
if(c[i].v>c[L].v)cnt--;else if(c[i].v<c[L].v)cnt++;}
for(int i=num[L]+1;i<=num[R]-1;i++)
{
cnt+=( (a+r[i]+1) - upper_bound(a+l[i],a+r[i]+1,c[R]) );//必须单关键字比较
cnt-=( lower_bound(a+l[i],a+r[i]+1,c[R]) - (a+l[i]) );
cnt-=( (a+r[i]+1) - upper_bound(a+l[i],a+r[i]+1,c[L]) );
cnt+=( lower_bound(a+l[i],a+r[i]+1,c[L]) - (a+l[i]) );
}
}
ans+=cnt;return ans;
}
int main()
{
LiSan();Get_First_Ans();makeblock();Sort_Each_Block();m=G();
for(int i=1;i<=m;i++){x=G();y=G();if(x>y)swap(x,y);P(Query(x,y));}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: