P15352
Gaochenxi103_QAQ · · 题解
做法
一个经典的 trick。
首先对于
观察主要问题在于操作
有没有什么高效的结构可以处理这个。于是就有了并查集加 ST 表的处理。新增一个数组
然后如果
实现一个函数merge(x,y,k),表示处理
- 如果
f_{x,k}=f_{y,k} ,直接结束函数。说明之后的操作已处理。 - 接下来如果
k=0 ,直接做x,y 的普通合并。与暴力代码一致。 - 否则考虑下放合并指令
merge(x,y,k-1)和merge(x+(1<<(k-1)),y+(1<<(k-1)),k-1)。 - 最后
f_{x,k} 和f_{y,k} 合并,表示两者之间已经互相操作过了。
每次查询的
int l,r,len;cin>>l>>r>>len;
int k=log2(len);
merge(l,r,k);
merge(l+len-1-(1<<k)+1,r+len-1-(1<<k)+1,k);
对于查询,和暴力代码一致。
注意到每层
总结一下:用分治的思想,巧妙的节省了大量无意义合并。
代码
::::success[Accepted code]
#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int N=2e5+10;
int n,fa[20][N],mx[N],mi[N],T;
void init()
{
for(int i=0;i<=19;i++)
{
for(int j=1;j<=n;j++)
{
fa[i][j]=j;
}
}
for(int i=1;i<=n;i++)mx[i]=mi[i]=i;
}
int findmi(int x)
{
if(mi[x]==x)return x;
return mi[x]=findmi(mi[x]);
}
int findmx(int x)
{
if(mx[x]==x)return x;
return mx[x]=findmx(mx[x]);
}
int find(int x,int k)
{
if(fa[k][x]==x)return x;
return fa[k][x]=find(fa[k][x],k);
}
void merge(int x,int y,int k)
{
int fx=find(x,k),fy=find(y,k);
if(fx==fy)return;
if(k==0)
{
fx=findmi(x),fy=findmi(y);
if(fx>fy)mi[fx]=fy;
else mi[fy]=fx;
fx=findmx(x),fy=findmx(y);
if(fx<fy)mx[fx]=fy;
else mx[fy]=fx;
return ;
}
merge(x,y,k-1);
merge(x+(1<<(k-1)),y+(1<<(k-1)),k-1);
fa[k][fy]=fx;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>T;
init();
while(T--)
{
int op;cin>>op;
if(op==1)
{
int x;cin>>x;
cout<<findmi(x)<<" "<<findmx(x)<<"\n";
}
else
{
int l,r,len;cin>>l>>r>>len;
int k=log2(len);
merge(l,r,k);
merge(l+len-1-(1<<k)+1,r+len-1-(1<<k)+1,k);
}
}
return 0;
}
::::