并查集重学
并查集重学
好多高级的操作,发现我学的那个并查集就是垃圾并查集,看点高级东西
可撤销并查集
这个就不能路径压缩了,只能按秩合并
撤销操作就是用一个栈记录所有的合并,撤销的时候全倒出来就好了
注意时间顺序,保证不了就用线段树分治(就是把连边的时间区域覆盖在线段树上,然后在线段树上\(dfs\))
code
struct D{
int fa[N],siz[N],top;
pair<int,int> sta[N];
void init(int n){fo(i,1,n)fa[i]=i,siz[i]=1;}
int find(int x){return fa[x]==x?x:find(fa[x]);}
int merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy)return 0;
if(siz[fx]>siz[fy])swap(fx,fy);
fa[fx]=fy;siz[fy]+=siz[fx];
sta[++top]=make_pair(fx,fy);
return 1;
}
void cancel(){
fa[sta[top].first]=sta[top].first;
siz[sta[top].second]-=siz[sta[top].first];
top--;return ;
}
}d[2];
例题
这个就是线段树分治,然后,直接两个并查集一起跑就好了
code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
return s*t;
}
const int N=40005;
struct D{
int fa[N],siz[N],top;
pair<int,int> sta[N];
void init(int n){fo(i,1,n)fa[i]=i,siz[i]=1;}
int find(int x){return fa[x]==x?x:find(fa[x]);}
int merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy)return 0;
if(siz[fx]>siz[fy])swap(fx,fy);
fa[fx]=fy;siz[fy]+=siz[fx];
sta[++top]=make_pair(fx,fy);
return 1;
}
void cancel(){
fa[sta[top].first]=sta[top].first;
siz[sta[top].second]-=siz[sta[top].first];
top--;return ;
}
}d[2];
int n,m,jz[205][205],s[N];
pair<int,int> ans[N];
bool pd(int x,int y){return x<=n&&x>0&&y<=n&&y>0;}
int id(int x,int y){return (x-1)*n+y;}
struct X{
#define ls x<<1
#define rs x<<1|1
vector<pair<int,int> > vec[2][N*4];
void ins(int x,int l,int r,int ql,int qr,pair<int,int> v,int t){
if(ql>qr)return ;
if(ql<=l&&r<=qr){
vec[t][x].push_back(v);
return ;
}
int mid=l+r>>1;
if(ql<=mid)ins(ls,l,mid,ql,qr,v,t);
if(qr>mid)ins(rs,mid+1,r,ql,qr,v,t);
return ;
}
void dfs(int x,int l,int r,int s0,int s1){
int n0=0,n1=0;
for(pair<int,int> i:vec[0][x])n0+=d[0].merge(i.first,i.second);
for(pair<int,int> i:vec[1][x])n1+=d[1].merge(i.first,i.second);
s0-=n0;s1-=n1;
if(l==r){
// ans[l]=make_pair(s0,s1);
ans[l]=make_pair(s0-s[l],s1-(n*n-s[l]));
while(n0--)d[0].cancel();
while(n1--)d[1].cancel();
return ;
}
int mid=l+r>>1;
dfs(ls,l,mid,s0,s1);
dfs(rs,mid+1,r,s0,s1);
while(n0--)d[0].cancel();
while(n1--)d[1].cancel();
return ;
}
#undef ls
#undef rs
}x;
map<pair<int,int>,pair<int,int>> mp;
void work(int a,int b,int c,int d,int i){
if(!pd(a,b)||!pd(c,d))return ;
if(jz[a][b]!=jz[c][d]){
x.ins(1,0,m,mp[make_pair(id(a,b),id(c,d))].first,i-1,make_pair(id(a,b),id(c,d)),mp[make_pair(id(a,b),id(c,d))].second);
mp.erase(make_pair(id(a,b),id(c,d)));
}
if(jz[a][b]==jz[c][d]){
mp.insert(make_pair(make_pair(id(a,b),id(c,d)),make_pair(i,jz[a][b])));
}
}
signed main(){
n=read();
fo(i,1,n)fo(j,1,n)jz[i][j]=read(),jz[i][j]?s[0]++:s[0];
fo(i,1,n)fo(j,1,n){
if(pd(i+1,j)&&jz[i][j]==jz[i+1][j])mp.insert(make_pair(make_pair(id(i,j),id(i+1,j)),make_pair(0,jz[i][j])));
if(pd(i,j+1)&&jz[i][j]==jz[i][j+1])mp.insert(make_pair(make_pair(id(i,j),id(i,j+1)),make_pair(0,jz[i][j])));
}
m=read();
fo(i,1,m){
int x=read(),y=read();jz[x][y]^=1;
work(x-1,y,x,y,i);
work(x,y-1,x,y,i);
work(x,y,x+1,y,i);
work(x,y,x,y+1,i);
s[i]=s[i-1];
jz[x][y]?s[i]++:s[i]--;
}
for(pair<pair<int,int>,pair<int,int> > i:mp)x.ins(1,0,m,i.second.first,m,i.first,i.second.second);
d[0].init(n*n);d[1].init(n*n);x.dfs(1,0,m,n*n,n*n);
fo(i,1,m)printf("%d %d\n",ans[i].second,ans[i].first);
}
类似的题,不过这个比上一个更加简单
可持久化并查集
可持久化数组+并查集
众所周知,主席树又名可持久化数组
于是我们用主席树维护每一个版本的\(fa\)和\(dep\)
按秩合并,复杂度\(\mathcal{O(nlog^2n)}\)
code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
return s*t;
}
const int N=1e5+5;
int n,m;
int rt[N];
struct Z{
struct POT{
int fa,dep,ls,rs;
}tr[N*62];
int seg;
void build(int &x,int l,int r){
x=++seg;
if(l==r)return tr[x].fa=l,tr[x].dep=1,void();
int mid=l+r>>1;
build(tr[x].ls,l,mid);
build(tr[x].rs,mid+1,r);
}
void ins(int pr,int &x,int l,int r,int pos,int f,int d){
x=++seg;tr[x]=tr[pr];
if(l==r)return tr[x].fa=f,tr[x].dep=d,void();
int mid=l+r>>1;
if(pos<=mid)ins(tr[pr].ls,tr[x].ls,l,mid,pos,f,d);
else ins(tr[pr].rs,tr[x].rs,mid+1,r,pos,f,d);
}
int query(int x,int l,int r,int pos){
if(l==r)return x;
int mid=l+r>>1;
if(pos<=mid)return query(tr[x].ls,l,mid,pos);
else return query(tr[x].rs,mid+1,r,pos);
}
}z;
int find(int rt,int x){
int now=z.query(rt,1,n,x);
if(z.tr[now].fa==x)return x;
else return find(rt,z.tr[now].fa);
}
int cur;
signed main(){
n=read();m=read();
z.build(rt[0],1,n);
fo(q,1,m){
int tp=read(),a=read(),b;
if(tp==1){
b=read();int fx=find(rt[q-1],a),fy=find(rt[q-1],b);
if(fx==fy)rt[q]=rt[q-1];
else {
int nx=z.query(rt[q-1],1,n,fx),ny=z.query(rt[q-1],1,n,fy);
if(z.tr[nx].dep>z.tr[ny].dep)swap(nx,ny),swap(fx,fy);
if(z.tr[nx].dep==z.tr[ny].dep){
z.ins(rt[q-1],rt[q],1,n,fx,fy,z.tr[nx].dep);
z.ins(rt[q],rt[q],1,n,fy,fy,z.tr[ny].dep+1);
}
else z.ins(rt[q-1],rt[q],1,n,fx,fy,z.tr[nx].dep);
}
}
if(tp==2)rt[q]=rt[a];
if(tp==3){
b=read();rt[q]=rt[q-1];
int fx=find(rt[q],a),fy=find(rt[q],b);
if(fx==fy)printf("1\n");
else printf("0\n");
}
}
}
例题
这个题就是可以单源最短路+可持久化并查集,查找最近的就行了
代码没写,咕了......
奇奇怪怪零零散散的并查集小清新
神奇的\(kruskal\),神奇的性质:
不同权值之间互不影响,除非是权值大的边连接了一个所有边权值都比他小的连通块(也就是永远不能加进去的情况)
1、某一权值的边的数量一定(在任意合法的最小生成树中)
某一条边如果可以被替换,那么一定满足两条边权值相等,那么一换一,数量不变
2、将小于某一值的边全部加入到生成树中,生成树的形态一定,连通性一定
两条边可以互相替换,首先权值一定相等,其次链接的联通块是同一个,那么连通性不变
于是这个题就可以利用这个性质
我们将权值相等的一起处理,因为权值不相等的互不影响,由上面性质可得
由上面性质可得,当前权值加进去之前,连通性一定,也就是说,当前权值的边连起来只要不会构成环,那么一定可以在同一颗生成树中
当然,如果这个权值连接的两点被权值比它小的边连接的话,那就永远不能在生成树里了
于是我们预处理每一条边连接的两个连通块,直接判断就行了
code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
return s*t;
}
const int N=5e5+5;
int n,m,q,qus[N];
int fai[N];
void init(int n){fo(i,1,n)fai[i]=i;}
int find(int x){return fai[x]==x?x:fai[x]=find(fai[x]);}
struct E{int x,y,v,id;}e[N],d[N];
bool com(E a,E b){return a.v<b.v;}
bool come(E a,E b){return a.id<b.id;}
bool comp(int x,int y){return e[x].v<e[y].v;}
int sta[N],top;
signed main(){
n=read();m=read();
fo(i,1,m)e[i].x=read(),e[i].y=read(),e[i].v=read(),e[i].id=i;
sort(e+1,e+m+1,com);init(n);
fo(i,1,m){
if(e[i].v!=e[i-1].v){
int j=i;
do{
d[e[j].id].x=find(e[j].x);
d[e[j].id].y=find(e[j].y);
j++;
}while(e[j-1].v==e[j].v&&j<=m);
}
int fx=find(e[i].x),fy=find(e[i].y);
if(fx==fy)continue;
if(fx<fy)swap(fx,fy);
fai[fx]=fy;
}
sort(e+1,e+m+1,come);
q=read();init(n);
while(q--){
int k=read();bool flag=true;
fo(i,1,k)qus[i]=read();
sort(qus+1,qus+k+1,comp);
fo(i,1,k){
int j=i;
do{
int fx=find(d[qus[j]].x),fy=find(d[qus[j]].y);
if(fx==fy){flag=false;break;}
if(fx<fy)swap(fx,fy);
sta[++top]=fx;fai[fx]=fy;j++;
}while(e[qus[j-1]].v==e[qus[j]].v&&j<=k);
while(top)fai[sta[top]]=sta[top--];
if(!flag)break;i=j-1;
}
if(flag)printf("YES\n");
else printf("NO\n");
}
}
QQ:2953174821