2022牛客多校 第8场 G Lexicographic Comparison(平衡树维护环/置换)
传送门
\(n\)个数的排列的置换会形成多个环,改变\(p_i\)会改变置换的环的情况,两个修改位置在同一个环里,环会分成两个。否则会合并两个环。
只需要知道这些环的情况。挨个检查每一个环的下标最靠前的位置上的数在\(x\)和\(y\)轮的情况就行了。
由此可以知道\(a_i\)并不重要。
但是上面的思路中因为环的数量较多,对一个环的判断时间较长,无法解决问题。
可以发现只需要一个长度的所有环的下标最靠前位置上的数在\(x\)和\(y\)轮的情况就行了,因为如果这个位置相等,其他同长度的环的所有位置一定相等,不相等的话就没有意义往后比较了。所以只需要最前面的位置上的数。
进一步可以发现,所有长度可以整除\(y-x\)的环的位置上的数在\(x\)和\(y\)轮的情况必然完全相同,否则一定不相同。
所以就是比较所有长度不可以整除\(y-x\)的环的最靠前位置上的数在\(x\)和\(y\)轮的情况。
对于每一类长度相同的环记录所有环中的最靠前位置。因为环的种类最多有\(\sqrt{n}\)种,每次询问,枚举所有长度的环,然后找出在\(x\)和\(y\)轮不相等环上的最靠前位置,比较这个位置上的在\(x\)和\(y\)轮的数就行了。
想到在每一个位置维护以这个位置为最靠前位置的环(有的位置可能为空)。这样就能快速找到对应的环,然后找到第\(x\)和\(y\)轮的数。
用平衡树维护每个环,每个环以一个剪开后的序列的形式维护,然后环的合并和分离手玩一下就是序列的区间操作。
然后需要快速找到这个环中最小下标,在维护的序列中的位置,然后分别后推\(x-1\)和\(y-1\)个位置就能知道对应的数了。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<ctime>
#include<cstdlib>
using namespace std;
#define int long long
const int N=1e5+100;
int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
set<int> st[N];
int tot,id[N],mn[N],fa[N],ch[N][2],rad[N],Size[N];
int new_node(int x){
int now=++tot;
id[now]=x;
mn[now]=x;
fa[now]=0;
ch[now][0]=ch[now][1]=0;
rad[now]=rand();
Size[now]=1;
return now;
}
void update(int now){
mn[now]=id[now];
Size[now]=1;
if(ch[now][0])mn[now]=min(mn[now],mn[ch[now][0]]),Size[now]+=Size[ch[now][0]];
if(ch[now][1])mn[now]=min(mn[now],mn[ch[now][1]]),Size[now]+=Size[ch[now][1]];
}
int merge(int x,int y){
if(x==0||y==0)return x+y;
if(rad[x]>rad[y]){
ch[x][1]=merge(ch[x][1],y);
update(x);
fa[ch[x][1]]=x;
return x;
}
else{
ch[y][0]=merge(x,ch[y][0]);
update(y);
fa[ch[y][0]]=y;
return y;
}
}
void split(int now,int &x,int &y,int k){
if(now==0){
x=y=0;
return;
}
if(Size[ch[now][0]]<k){
x=now;
fa[ch[now][1]]=0;
split(ch[now][1],ch[x][1],y,k-Size[ch[now][0]]-1);
if(ch[x][1])fa[ch[x][1]]=x;
update(x);
}
else{
y=now;
fa[ch[now][0]]=0;
split(ch[now][0],x,ch[y][0],k);
if(ch[y][0])fa[ch[y][0]]=y;
update(y);
}
}
int get_root(int x){
if(fa[x]==0)return x;
else return get_root(fa[x]);
}
int get_rank(int x,int root){
int ans=Size[ch[x][0]]+1;
while(x!=root){
if(ch[fa[x]][1]==x)ans+=Size[ch[fa[x]][0]]+1;
x=fa[x];
}
return ans;
}
void st_del(int len,int first){
st[len].erase(first);
if(st[len].size()==0)st[0].erase(len);
}
void st_ins(int len,int first){
if(st[len].size()==0)st[0].insert(len);
st[len].insert(first);
}
void clear(int n){
for(int i=0;i<=n;i++)st[i].clear();
tot=0;
}
int get_kth(int k,int now){
int l=ch[now][0];
if(Size[l]>=k)return get_kth(k,l);
else if(Size[l]+1>=k)return now;
else return get_kth(k-Size[l]-1,ch[now][1]);
}
int root[N],a[N];
int X,Y,Z;
char s[100];
signed main(){
srand(time(NULL));
int T=read();
while(T--){
int n=read(),q=read();
for(int i=1;i<=n;i++)a[i]=i,root[i]=new_node(i);
st[0].insert(1);
for(int i=1;i<=n;i++)st[1].insert(i);
while(q--){
scanf("%s",s+1);
int x=read(),y=read();
if(s[1]=='c'){
x--,y--;
if(x==y){
printf("=\n");
continue;
}
bool rev=0;
int pos=n+1,len;
if(x>y)swap(x,y),rev=1;
bool flag=0;
for(auto i=st[0].begin();i!=st[0].end();i++){
if((y-x)%*i==0)continue;
flag=1;
if(*st[*i].begin()<pos){
pos=*st[*i].begin();
len=*i;
}
}
if(flag==0){
printf("=\n");
continue;
}
int rk=get_rank(mn[root[pos]],root[pos]);
int w_x=a[get_kth((rk+x%len-1)%len+1,root[pos])];
int w_y=a[get_kth((rk+y%len-1)%len+1,root[pos])];
if((w_x<w_y)^rev)printf("<\n");
else printf(">\n");
}
else{
if(x==y)continue;
if(s[6]=='a')swap(a[x],a[y]);
else{
int x_root=get_root(x);
int y_root=get_root(y);
int x_rk=get_rank(x,x_root);
int y_rk=get_rank(y,y_root);
if(x_root==y_root){
st_del(Size[x_root],mn[x_root]);
split(x_root,X,Z,max(x_rk,y_rk));
split(X,X,Y,min(x_rk,y_rk));
root[mn[Y]]=Y;
st_ins(Size[root[mn[Y]]],mn[Y]);
int Mn=n+1;
if(X)Mn=min(Mn,mn[X]);
if(Z)Mn=min(Mn,mn[Z]);
root[Mn]=merge(X,Z);
st_ins(Size[root[Mn]],Mn);
}
else{
int Mn=min(mn[x_root],mn[y_root]);
st_del(Size[x_root],mn[x_root]);
st_del(Size[y_root],mn[y_root]);
split(y_root,X,Y,y_rk);
Y=merge(Y,X);
split(x_root,X,Z,x_rk);
root[Mn]=merge(merge(X,Y),Z);
st_ins(Size[root[Mn]],Mn);
}
}
}
}
clear(n);
}
return 0;
}
/*
1
6 7
swap_p 3 6
swap_p 3 5
swap_p 5 6
swap_p 4 2
swap_a 1 4
cmp 34 81
cmp 87 76
*/