【P5385】【Cnoi2019】须臾幻境(LCT)

题面:

n n n 个点,有一个长度为 m m m 的边序列 A A A q q q 次询问由这 n n n 个点和 A l , ⋯   , A r A_l,\cdots,A_r Al,,Ar 的边构成的图中的连通块数量。强制在线。

n , q ≤ 1 0 5 n,q\leq 10^5 n,q105 m ≤ 2 × 1 0 5 m\leq 2\times 10^5 m2×105

题解:

在线也能把它搞成离线:枚举右端点 r r r 右移,并维护 A 1 , ⋯   , A r A_1,\cdots,A_r A1,,Ar 组成的以下标为边权的最大生成森林,以及一棵可持久化线段树维护每条边是否在生成树内。

然后询问的时候我们直接在可持久化线段树上问 A 1 , ⋯   , A r A_1,\cdots,A_r A1,,Ar 组成的最大生成森林中有多少条边编号在 [ l , r ] [l,r] [l,r] 内,即可得到 A l , ⋯   , A r A_l,\cdots,A_r Al,,Ar 的生成森林中边的数量。然后用点减边即可得到连通块数。

时间复杂度 O ( ( m + q ) log ⁡ m ) O((m+q)\log m) O((m+q)logm)

#include<bits/stdc++.h>

#define N 1000010
#define M 2000010
#define INF 0x7fffffff

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	} 
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

struct edge
{
	int u,l,r;
}e[M];

int n,m,q,rt[M];
bool t;

namespace Seg
{
	#define lc(u) ch[u][0]
	#define rc(u) ch[u][1]
	const int NN=10000000;
	int node,ch[NN][2],sum[NN];
	int copy(int lst){node++,lc(node)=lc(lst),rc(node)=rc(lst),sum[node]=sum[lst];return node;}
	void up(int u){sum[u]=sum[lc(u)]+sum[rc(u)];}
	void update(int &u,int lst,int l,int r,int x)
	{
		u=copy(lst);
		if(l==r) return (void)(sum[u]^=1);
		int mid=(l+r)>>1;
		if(x<=mid) update(lc(u),lc(lst),l,mid,x);
		else update(rc(u),rc(lst),mid+1,r,x);
		up(u);
	}
	int query(int u,int l,int r,int ql)
	{
		if(!u) return 0;
		if(ql<=l) return sum[u];
		int mid=(l+r)>>1,ans=0;
		if(ql<=mid) ans+=query(lc(u),l,mid,ql);
		return ans+query(rc(u),mid+1,r,ql);
	}
	#undef lc
	#undef rc
}

namespace LCT
{
	#define lc(u) t[u].ch[0]
	#define rc(u) t[u].ch[1]
	const int NN=N+M;
	struct data
	{
		int val,pos;
		data(){};
		data(int _v,int _p){val=_v,pos=_p;}
	};
	bool operator < (const data &a,const data &b){return a.val<b.val;}
	struct Tree
	{
		int fa,ch[2];
		bool rev;
		data val,sum;
	}t[NN];
	int node;
	void up(int u){t[u].sum=min(t[u].val,min(t[lc(u)].sum,t[rc(u)].sum));}
	int newnode(int v){++node,t[node].val=data(v,node),up(node);return node;}
	void downn(int u){swap(lc(u),rc(u)),t[u].rev^=1;}
	void down(int u){if(t[u].rev) downn(lc(u)),downn(rc(u)),t[u].rev=0;}
	bool get(int u){return rc(t[u].fa)==u;}
	bool notroot(int u){return lc(t[u].fa)==u||rc(t[u].fa)==u;}
	void rotate(int u)
	{
		int fa=t[u].fa,gfa=t[fa].fa; bool d1=get(u);
		if(notroot(fa)) t[gfa].ch[get(fa)]=u; t[u].fa=gfa;
		t[t[fa].ch[d1]=t[u].ch[d1^1]].fa=fa;
		t[t[u].ch[d1^1]=fa].fa=u; up(fa);
	}
	void splay(int u)
	{
		static int sta[NN];
		int top=1,now=sta[top]=u;
		while(notroot(now)) sta[++top]=now=t[now].fa;
		while(top) down(sta[top]),top--;
		while(notroot(u))
		{
			int fa=t[u].fa;
			if(notroot(fa)) rotate((get(u)^get(fa))?u:fa);
			rotate(u);
		}
		up(u);
	}
	void access(int u)
	{
		for(int y=0;u;y=u,u=t[u].fa)
			splay(u),rc(u)=y,up(u);
	}
	void makeroot(int u)
	{
		access(u),splay(u),downn(u);
	}
	int findleft(int u)
	{
		while(1)
		{
			down(u);
			if(!lc(u)) return u;
			u=lc(u);
		}
	}
	int findroot(int u)
	{
		access(u),splay(u);
		int rt=findleft(u);
		splay(rt); return rt;
	}
	data query(int u,int v)
	{
		makeroot(u);
		if(findroot(v)!=u) return data(-1,114514);
		return t[u].sum;
	}
	void link(int u,int v)
	{
		access(u),splay(u),makeroot(v);
		t[rc(u)=v].fa=u,up(u);
	}
	void cut(int u,int v)
	{
		makeroot(v),makeroot(u);
		rc(u)=t[v].fa=0,up(u);
	}
	int update(int u,int v,int tim)
	{
		rt[tim]=rt[tim-1];
		int now=newnode(tim);
		data ptim=query(u,v);
		if(ptim.val==-1)
		{
			link(u,now),link(now,v);
			Seg::update(rt[tim],rt[tim],1,m,tim);
		}
		else if(tim>ptim.val)
		{
			int id=ptim.pos-n;
			cut(e[id].l,e[id].u),cut(e[id].u,e[id].r);
			link(u,now),link(now,v);
			Seg::update(rt[tim],rt[tim],1,m,id);
			Seg::update(rt[tim],rt[tim],1,m,tim);
		}
		return now;
	}
	#undef lc
	#undef rc
}

int main()
{
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	n=LCT::node=read(),m=read(),q=read(),t=read();
	for(int i=0;i<=n;i++) LCT::t[i].val=LCT::t[i].sum=LCT::data(INF,114514);
	for(int i=1;i<=m;i++)
	{
		e[i].l=read(),e[i].r=read();
		e[i].u=LCT::update(e[i].l,e[i].r,i);
	}
	int lans=0;
	while(q--)
	{
		int l=read(),r=read();
		if(t>0) l=(l+t*lans)%m+1,r=(r+t*lans)%m+1;
		if(l>r) swap(l,r);
		printf("%d\n",lans=(n-Seg::query(rt[r],1,m,l)));
	}
	return 0;
}
/*
3 3 3 0
1 2
2 3
1 3
1 1
2 2
2 3
*/
/*
4 5 114514 0
1 2
2 3
3 4
1 3
2 4
*/
/*
4 3 114514 0
1 2
2 3
3 4
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值