线段树分治[模板]
线段树分治,是一种离线后在时间轴上用线段树维护区间操作,并可以进行撤销的思想。
二分图 /【模板】线段树分治
\(\text{Solution}:\)
二分图的充要条件是不存在奇环,可以用扩展域并查集维护。把 \([l,r]\) 区间内的边分为 \(\log\) 段挂在线段树的对应节点上,遍历到这个节点时把所有边合并并完成查询。由于并查集需要可撤销,故需要按秩合并或者启发式合并。
\(\text{Code}:\)
#include <bits/stdc++.h>
//#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std; const int N=200010;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,m,K,U[N],V[N],f[N],d[N];
vector<int> e[N<<2];
pair<int,int> sta[N<<3]; int tp;
#define lc (x<<1)
#define rc (x<<1|1)
void Insert(int u,int v,int l,int r,int x,int id)
{
if(l>=u&&r<=v)
{
e[x].eb(id);
return;
}
int mid=(l+r)/2;
if(u<=mid) Insert(u,v,l,mid,lc,id);
if(v>mid) Insert(u,v,mid+1,r,rc,id);
}
inline int Find(int x)
{
while(x^f[x]) x=f[x];
return x;
}
inline void Merge(int x,int y)
{
int fx=Find(x), fy=Find(y);
if(fx==fy) return;
if(d[fx]>d[fy]) swap(fx,fy);
sta[++tp]=mk(fx,d[fx]==d[fy]);
f[fx]=fy, d[fy]+=d[fx]==d[fy];
}
void Get(int x,int l,int r)
{
int flg=1;
int pre=tp;
for(ri int i=0;i<(int)e[x].size();i++)
{
int fx=Find(U[e[x][i]]), fy=Find(V[e[x][i]]);
if(fx==fy)
{
for(ri int j=l;j<=r;j++) puts("No");
flg=0;
break;
}
Merge(U[e[x][i]]+n,fy), Merge(V[e[x][i]]+n,fx);
}
if(flg)
{
if(l==r) puts("Yes");
else
{
int mid=(l+r)/2;
Get(lc,l,mid), Get(rc,mid+1,r);
}
}
while(tp>pre)
{
pair<int,int> p=sta[tp];
d[f[p.fi]]-=p.se, f[p.fi]=p.fi;
sta[tp--]=mk(0,0);
}
}
signed main()
{
n=read(), m=read(), K=read();
for(ri int i=1;i<=m;i++)
{
U[i]=read(), V[i]=read();
int l,r;
l=read(), r=read();
Insert(l+1,r,1,K,1,i);
}
for(ri int i=1;i<=n+n;i++) f[i]=i;
Get(1,1,K);
return 0;
}
「离线可过」动态图连通性
\(\text{Code}:\)
#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std; const int N=5010, M=500010;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,m,T,f[N],siz[N],Ans[M];
vector<int> e[M<<2];
unordered_map<int,int> In[N];
struct Temp
{
int u,v,l,r;
}tmp[M]; int cnt;
int U[M],V[M];
int sta[M<<2],tp;
#define lc (x<<1)
#define rc (x<<1|1)
void Insert(int u,int v,int l,int r,int x,int id)
{
if(l>=u&&r<=v)
{
e[x].eb(id);
return;
}
int mid=(l+r)/2;
if(u<=mid) Insert(u,v,l,mid,lc,id);
if(v>mid) Insert(u,v,mid+1,r,rc,id);
}
inline int Find(int x)
{
while(x^f[x]) x=f[x];
return x;
}
inline void Merge(int x,int y)
{
int fx=Find(x), fy=Find(y);
if(fx==fy) return;
if(siz[fx]>siz[fy]) swap(fx,fy);
f[fx]=fy, siz[fy]+=siz[fx];
sta[++tp]=fx;
}
void Get(int x,int l,int r)
{
int pre=tp;
for(ri int i=0;i<(int)e[x].size();i++) Merge(tmp[e[x][i]].u,tmp[e[x][i]].v);
if(l==r)
{
if(Find(U[l])==Find(V[l])) puts("Y");
else puts("N");
}
else
{
int mid=(l+r)/2;
Get(lc,l,mid), Get(rc,mid+1,r);
}
while(tp>pre)
{
int p=sta[tp];
siz[f[p]]-=siz[p];
f[p]=p;
sta[tp--]=0;
}
}
#undef lc
#undef rc
signed main()
{
n=read(), m=read();
for(ri int i=1;i<=m;i++)
{
int opt,x,y;
opt=read(), x=read(), y=read();
if(x>y) swap(x,y);
if(!opt) In[x][y]=T+1;
else if(opt==1)
{
if(In[x][y]<=T) tmp[++cnt]=(Temp){x,y,In[x][y],T};
In[x].erase(In[x].find(y));
}
else U[++T]=x, V[T]=y;
}
if(!T) return 0;
for(ri int i=1;i<=cnt;i++) Insert(tmp[i].l,tmp[i].r,1,T,1,i);
for(ri int i=1;i<=n;i++)
{
for(auto j:In[i])
{
tmp[++cnt]=(Temp){i,j.fi,j.se,T};
Insert(tmp[cnt].l,tmp[cnt].r,1,T,1,cnt);
}
}
for(ri int i=1;i<=n;i++) f[i]=i, siz[i]=1;
Get(1,1,T);
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。