P1975 [国家集训队]排队 题解

问题描述

P1975 [国家集训队]排队

求每次序列两个数交换过后的逆序对数

问题求解

我们能发现,假设我们交换 \(l,r\) 两个数,,那么显然从 \(1\) 到 \(l-1\) 和 \(r+1\) 到 \(N\) 的数是不受影响的,那么我们只需要讨论 \(l,r\) 之间的就好了

我们先考虑删去数,删去 \(l,r\) 中在 \([1,a[r]-1]\) 之间的数 ,和 \([a[r]+1,MAXN]\) 之间的数,因为原来他们产生了贡献

然后加上贡献, \(l,r\) 中在\([1,a[r]+1]\) 和 \([a[l],MAXN]\) 之间的数

然后看 \(a[l],a[r]\) 是否产生贡献,加上一些小细节就好了

在一个区间里面查询一段数值的数,树状数组套权值线段树就好了

代码实现

代码不长

#include<bits/stdc++.h>
const int maxn=2e5+5;
int tot,N,hsh[maxn],a[maxn],root[maxn],Ans,c[maxn],sz,M;
struct node{
	int sum,ls,rs;
}tree[maxn*40];
struct IO{
    static const int S=1<<21;
    char buf[S],*p1,*p2;int st[105],Top;
    ~IO(){clear();}
    inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
    inline void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
    inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    inline IO&operator >> (char&x){while(x=gc(),x==' '||x=='\n'||x=='r');return *this;}
    template<typename T>inline IO&operator >> (T&x){
        x=0;bool f=0;char ch=gc();
        while(ch<'0'||ch>'9'){if(ch=='-') f^=1;ch=gc();}
        while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=gc();
        f?x=-x:0;return *this;
    }
    inline IO&operator << (const char c){pc(c);return *this;}
    template<typename T>inline IO&operator << (T x){
        if(x<0) pc('-'),x=-x;
        do{st[++st[0]]=x%10,x/=10;}while(x);
        while(st[0]) pc('0'+st[st[0]--]);
		return *this;
    }
}fin,fout;
inline void Bit_add(int x,int data){
	for(int i=x;i<=maxn;i+=i&-i) c[i]+=data;
	return ;
}
inline int Bit_get(int x){
	int ret=0;
	for(int i=x;i;i-=i&-i)ret+=c[i];
	return ret;
}
void modify(int &x,int l,int r,int pos,int v){
	if(!x)x=++sz;tree[x].sum+=v;
	if(l>=r)return ;
	int mid=(r-l>>1)+l;
	pos<=mid?modify(tree[x].ls,l,mid,pos,v):modify(tree[x].rs,mid+1,r,pos,v);
	return ;
}
int query(int x,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr) return tree[x].sum;
	int mid=(r-l>>1)+l;
	int ret=0;
	(ql<=mid)&&(ret+=query(tree[x].ls,l,mid,ql,qr),0);
	(qr>mid)&&(ret+=query(tree[x].rs,mid+1,r,ql,qr),0);
	return ret;
}
inline void add(int x,int y,int v){
	for(int i=x;i<=maxn;i+=i&-i) modify(root[i],1,tot,y,v);
}
inline int ask(int l,int r,int ql,int qr){
	if(l>r||ql>qr) return 0;
	int ret=0;l--;
	for(int i=r;i;i-=i&-i) ret+=query(root[i],1,tot,ql,qr);
	for(int i=l;i;i-=i&-i) ret-=query(root[i],1,tot,ql,qr);
	return ret;
}
int main(){
	freopen("P1975.in","r",stdin);
	freopen("P1975.out","w",stdout);
	fin>>N;
	for(int i=1;i<=N;i++)fin>>a[i],hsh[i]=a[i];
	std::sort(hsh+1,hsh+1+N);
	tot=std::unique(hsh+1,hsh+N+1)-hsh-1;
	for(int i=N;i;i--){
		a[i]=std::lower_bound(hsh+1,hsh+1+tot,a[i])-hsh;
		Ans+=Bit_get(a[i]-1);
		Bit_add(a[i],1);
	}
	for(int i=1;i<=N;i++) add(i,a[i],1);
	fin>>M;fout<<Ans<<'\n';
	for(int i=1;i<=M;i++){
		int l,r;fin>>l>>r;
		(l>r)&&(std::swap(l,r),0);
		Ans+=ask(l+1,r-1,1,a[r]-1);
		Ans-=ask(l+1,r-1,a[r]+1,tot);
		Ans+=ask(l+1,r-1,a[l]+1,tot);
		Ans-=ask(l+1,r-1,1,a[l]-1);
		(a[l]>a[r])&&(Ans--,0);
		(a[l]<a[r])&&(Ans++,0);
		add(l,a[l],-1),add(r,a[r],-1);
		add(l,a[r],1),add(r,a[l],1);
		std::swap(a[l],a[r]);
		fout<<Ans<<'\n';
	}
	return 0;
}
上一篇:洛谷P3714 [BJOI2017]树的难题 【点分治,线段树合并】


下一篇:#轮廓线dp,模型转换#洛谷 3226 [HNOI2012]集合选数