题解 CF785E 【Anton and Permutation】
一.闲谈
听说本题分块可以过而且吊打树套树?orz。。。我果然还是太菜了。。。
二.分析
1.求逆序对
简化题目:给出序列1-n,以及m个操作,每次交换两个数,求当前序列的逆序对数
如果,交换的两个数相同,我们直接输出当前答案即可,那么其他的情况呢?
假设,我们知道交换前的逆序对数,那么新序列的逆序对数便等于原逆序对数加上两个数交换后的贡献
那么,我们来考虑如何计算两个数交换的贡献
我们设两个数分别为x和y,我们再设find_max(l,r,x)表示区间l->r内比x大的数的个数,find_min(l,r,x)同理,再设a[x]表示x的位置
那么交换后,与x,y有关的逆序对为(注:这里a[x]与a[y]尚未交换,且以下不考虑x与y形成的逆序对):
find_max(1,a[y]-1,x)+find_min(a[y]+1,n,x)+find_max(1,a[x]-1,y)+find_min(a[x]+1,n,y)
那么我们加上这个答案就好了吗?
不对!思考下,交换前,可能有与x,y有关的逆序对,如果我们直接加上当前的逆序对的话,我们就可能重复计算了!所以,我们还需要把答案减去交换前的与x,y有关的逆序对数,即:
find_max(1,a[x]-1,x)+find_min(a[x]+1,n,x)+find_max(1,a[y]-1,y)+find_min(a[y]+1,n,y)
然后,我们再来考虑x,y形成的逆序对:
交换后,x与y有两个情况:
1.x与y形成一个逆序对:
我们直接将答案加一即可
2.x与y不形成逆序对,原逆序对消失:
那么,我们是否需要将答案减一呢?
不是的,因为,我们一开始,不是减去了与x,y有关的逆序对数嘛?这时,我们在x里减去了逆序对(x,y),又在y里减去了逆序对(x,y),而我们本来只需要减一次的却减了两次!所以我们也需要将答案加一!
所以,两种情况的处理方式是相同的!
2.求函数find_min、find_max
现在,我们已经知道如何求逆序对了,但是,我们还需要知道如何求find_min、find_max两个函数,我们首先考虑优秀的权值线段树,然而,全局线段树并不擅长区间,于是我们启用它的升级版:主席树,然后我们就可以轻松地求出来了,不过,我们这时,发现,我们还有交换操作没用,如果,我们写普通的主席树,我们就需要对a[x]>a[y]的每一个主席树都修改一遍,这时,我们再给它升级一下:我们用带修主席树来解决这些问题!
至于交换操作,我们可以如此理解:
将a[x]位置上的x修改成y,将a[y]位置上的y修改成x,之后我们再把a[x]与a[y]交换一下就可以了!
复杂度:O(mloglogn*巨大常数(雾))
三.优化
这题甚为毒瘤的一点在于:卡空间
经过试验(类似方法),开long long的直接凉
开int 2e7会MLE,开1e7会Re
于是我愤怒之下,写了一个垃圾回收,然后空间开成1e7+5e6才过的QwQ
注意:写垃圾回收时,不要把root[i]回收了,因为我们还需要查询值之类的操作。。。
四.代码
//#pragma GCC optimize()//手动Ox优化 #include<bits/stdc++.h> using namespace std; const int N=1e7+5e6; struct node{ int val,lson,rson; }t[N]; int n,m; int a[N]; int siz,root[N]; int Now[N],Pas[N],L,R; int sta[N],top; inline int lowbit(int x){ return x&-x; } inline int news(){ if(top){ return sta[top--]; } return ++siz; } inline void delt(int x){ sta[++top]=x; } //垃圾回收 inline void mest(int x){ t[x].lson=t[x].rson=t[x].val=0; } inline void insert(int &now,int l,int r,int x){ if(!now){ now=news(); mest(now); } t[now].val++; if(l==r){ return; } int mid=(l+r)>>1; if(x<=mid){ insert(t[now].lson,l,mid,x); }else{ insert(t[now].rson,mid+1,r,x); } } inline int find_Max(int l,int r,int lc,int rc){ int sum=0,tot=0; for(int i=1;i<=L;++i){ sum-=t[Pas[i]].val; tot-=t[t[Pas[i]].rson].val; } for(int i=1;i<=R;++i){ sum+=t[Now[i]].val; tot+=t[t[Now[i]].rson].val; } if(lc<=l&&r<=rc){ return sum; } int mid=(l+r)>>1; if(rc<=mid){ for(int i=1;i<=L;++i){ Pas[i]=t[Pas[i]].lson; } for(int i=1;i<=R;++i){ Now[i]=t[Now[i]].lson; } return find_Max(l,mid,lc,rc); } if(lc>mid){ for(int i=1;i<=L;++i){ Pas[i]=t[Pas[i]].rson; } for(int i=1;i<=R;++i){ Now[i]=t[Now[i]].rson; } return find_Max(mid+1,r,lc,rc); } for(int i=1;i<=L;++i){ Pas[i]=t[Pas[i]].lson; } for(int i=1;i<=R;++i){ Now[i]=t[Now[i]].lson; } return find_Max(l,mid,lc,rc)+tot; } inline int find_Min(int l,int r,int lc,int rc){ int sum=0,tot=0; for(int i=1;i<=L;++i){ sum-=t[Pas[i]].val; tot-=t[t[Pas[i]].lson].val; } for(int i=1;i<=R;++i){ sum+=t[Now[i]].val; tot+=t[t[Now[i]].lson].val; } if(lc<=l&&r<=rc){ return sum; } int mid=(l+r)>>1; if(rc<=mid){ for(int i=1;i<=L;++i){ Pas[i]=t[Pas[i]].lson; } for(int i=1;i<=R;++i){ Now[i]=t[Now[i]].lson; } return find_Min(l,mid,lc,rc); } if(lc>mid){ for(int i=1;i<=L;++i){ Pas[i]=t[Pas[i]].rson; } for(int i=1;i<=R;++i){ Now[i]=t[Now[i]].rson; } return find_Min(mid+1,r,lc,rc); } for(int i=1;i<=L;++i){ Pas[i]=t[Pas[i]].rson; } for(int i=1;i<=R;++i){ Now[i]=t[Now[i]].rson; } return find_Min(mid+1,r,lc,rc)+tot; } inline int find_min(int l,int r,int x){ if(!r){ return 0; } if(x==1){ return 0; } int now=l-1; L=R=0; while(now){ Pas[++L]=root[now]; now-=lowbit(now); } now=r; while(now){ Now[++R]=root[now]; now-=lowbit(now); } return find_Min(1,n,1,x-1); } inline int find_max(int l,int r,int x){//寻找l-r区间内,比x大的数 if(l==n+1){ return 0; } if(x==n){ return 0; } int now=l-1; L=R=0; while(now){ Pas[++L]=root[now]; now-=lowbit(now); } now=r; while(now){ Now[++R]=root[now]; now-=lowbit(now); } return find_Max(1,n,x+1,n); } inline void delted(int now,int l,int r,int x){ t[now].val--; if(l==r){ return; } int mid=(l+r)>>1; if(x<=mid){ delted(t[now].lson,l,mid,x); if(!t[t[now].lson].val){ delt(t[now].lson); t[now].lson=0; } return; }else{ delted(t[now].rson,mid+1,r,x); if(!t[t[now].rson].val){ delt(t[now].rson); t[now].rson=0; } } } inline void change(int t,int x,int y){//将t位置的x修改为y int now=t; while(now<=n){ delted(root[now],1,n,x); insert(root[now],1,n,y); now+=lowbit(now); } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i){ a[i]=i; int now=i; while(now<=n){ insert(root[now],1,n,i); now+=lowbit(now); } } long long ans=0; while(m--){ int x,y; scanf("%d%d",&x,&y); if(x>y){ swap(x,y); } if(x==y){ printf("%lld\n",ans); continue; } ans-=(find_max(1,a[x]-1,x)+find_min(a[x]+1,n,x)); ans-=(find_max(1,a[y]-1,y)+find_min(a[y]+1,n,y)); ans+=(find_max(1,a[y]-1,x)+find_min(a[y]+1,n,x)); ans+=(find_max(1,a[x]-1,y)+find_min(a[x]+1,n,y)); ans++; change(a[x],x,y),change(a[y],y,x); swap(a[x],a[y]); printf("%lld\n",ans); } return 0; }