【YbtOJ#731】图上询问
题目
题目链接:https://www.ybtoj.com.cn/problem/731
\(n,m,Q\leq 2\times 10^5\)。
思路
考场上写了一发 \(O(Q\sqrt{m}\alpha(n))\) 回滚莫队没卡过去。。。
考虑把询问按照右端点离线,我们对于询问 \([l,r]\),把 \([1,r]\) 的边依次加入 LCT 中并维护最大生成森林,那么我们只需要知道最大生成森林中编号 \(\geq l\) 的边的数量即可。
那么直接把每一条边看作一个点,可以用并查集维护点是否连通,如果不连通就直接在这两个点直接插入这条边所对应的点并 link 上,否则由于我们把边从小到大插入,只需要查询两点之间的路径点(指 LCT 上的点,也就是原图的点和边。我们可以把原图点的权值设为无限大)权值的最小值,删去这个点(原图的边)再插入新的即可。
可以用树状数组维护超过 \(l\) 的边的数量。
时间复杂度 \(O(n\log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=400010;
int n,m,Q,ans[N],U[N],V[N],father[N];
struct Query
{
int l,r,id;
}ask[N];
bool cmp(Query x,Query y)
{
return x.r<y.r;
}
int find(int x)
{
return x==father[x]?x:father[x]=find(father[x]);
}
struct BIT
{
int c[N];
void add(int x,int v)
{
for (int i=x;i<=m;i+=i&-i)
c[i]+=v;
}
int query(int x)
{
int ans=0;
for (int i=x;i;i-=i&-i)
ans+=c[i];
return ans;
}
}bit;
struct LCT
{
int val[N],minv[N],ch[N][2],fa[N];
bool rev[N];
bool pos(int x) { return x==ch[fa[x]][1]; }
bool notrt(int x) { return x==ch[fa[x]][0] || x==ch[fa[x]][1]; }
void pushup(int x)
{
minv[x]=min(val[x],min(minv[ch[x][0]],minv[ch[x][1]]));
}
void pushdown(int x)
{
if (rev[x])
{
int lc=ch[x][0],rc=ch[x][1];
swap(ch[lc][0],ch[lc][1]); rev[lc]^=1;
swap(ch[rc][0],ch[rc][1]); rev[rc]^=1;
rev[x]=0;
}
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=pos(x),c=ch[x][k^1];
if (notrt(y)) ch[z][pos(y)]=x; ch[x][k^1]=y; ch[y][k]=c;
if (c) fa[c]=y; fa[y]=x; fa[x]=z;
pushup(y); pushup(x);
}
void splay(int x)
{
stack<int> st; st.push(x);
for (int i=x;notrt(i);i=fa[i]) st.push(fa[i]);
for (;st.size();st.pop()) pushdown(st.top());
for (;notrt(x);rotate(x))
if (notrt(fa[x])) rotate((pos(x)==pos(fa[x]))?fa[x]:x);
}
void access(int x)
{
for (int y=0;x;y=x,x=fa[x])
{
splay(x); ch[x][1]=y;
pushup(x);
}
}
void makert(int x)
{
access(x); splay(x);
swap(ch[x][0],ch[x][1]); rev[x]^=1;
}
void split(int x,int y)
{
makert(x); access(y);
splay(y);
}
void link(int x,int y)
{
makert(x); fa[x]=y;
pushup(y);
}
void cut(int x,int y)
{
makert(x); access(y); splay(y);
ch[y][0]=fa[x]=0;
pushup(x); pushup(y);
}
}lct;
int main()
{
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
scanf("%d%d%d",&n,&m,&Q);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&U[i],&V[i]);
U[i]+=m; V[i]+=m;
}
for (int i=1;i<=Q;i++)
{
scanf("%d%d",&ask[i].l,&ask[i].r);
ask[i].id=i;
}
sort(ask+1,ask+1+Q,cmp);
for (int i=1;i<=n+m;i++)
father[i]=i,lct.val[i]=lct.minv[i]=i;
lct.minv[0]=2e9;
for (int i=1,j=1;i<=Q;i++)
{
for (;j<=ask[i].r;j++)
{
if (U[j]==V[j]) continue;
int x=find(U[j]),y=find(V[j]);
if (x==y)
{
lct.split(U[j],V[j]);
int p=lct.minv[V[j]];
lct.cut(p,U[p]); lct.cut(p,V[p]);
bit.add(p,-1);
}
else father[x]=y;
lct.link(j,U[j]); lct.link(j,V[j]);
bit.add(j,1);
}
int cnt=bit.query(m)-bit.query(ask[i].l-1);
ans[ask[i].id]=n-cnt;
}
for (int i=1;i<=Q;i++)
printf("%d\n",ans[i]);
return 0;
}