【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;
}
posted @ 2021-02-27 17:00  stoorz  阅读(46)  评论(0编辑  收藏  举报