[CF891C]Envy(离线+Dsu/在线LCT)

  本篇文章只介绍在该题被卡常的在线算法LCT,不介绍正常的std做法,如果想学习正常做法的,请出门右转…

题意

  给定一张 n ( n ≤ 500000 ) n(n\le 500000) n(n500000)个点 m ( m ≤ 500000 ) m(m\le 500000) m(m500000)条边的无向图,一共有 q ( q ≤ 500000 ) q(q\le 500000) q(q500000)次询问,每次询问给出一个边集,边集大小为 k ( ∑ k ≤ 500000 ) k(\sum k\le500000) k(k500000),问这个边集里的边是否都能出现在这张图的MST上。

分析

  一看到维护MST的题,也就可以想到可以用LCT来做,不过由于边数+点数有100w,所以很卡常。
  首先,我们对所有边跑一边Kruskal,找出一棵该图的MST,然后对于每次询问,我们对于一条边,询问这条边两个点间权值最大的边是哪一条。显然那条权值最大的边不可能权值大于询问的边,因为这样我们在找MST的时候断开那条边,然后加入这条边是更优的。
  如果权值最大的边的权值小于询问的边,说明询问的边是不会出现在MST上的,可以直接break输出NO;如果权值最大的边的权值等于询问的边,说明这两条边哪条边在MST上都是一样的,我们为了要让询问的边都出现在MST上,那么我们就断开原来那条边,之后把现在的这条边加入到MST上,再把权值改成0(防止之后的查询操作将这条边删去),在做完一次询问后将权值还原回来就好了。
  复杂度大概是 O ( m log ⁡ m + q log ⁡ ( n + m ) ) O(m\log m+q\log(n+m)) O(mlogm+qlog(n+m))的(我不太会算LCT复杂度),不过常数很大,加了fread才能通过此题。

Code

#pragma GCC optimize(3,"Ofast","inline")
#include
using namespace std;
typedef long long ll;
bool Finish_read;
static char buf[30 << 20],*p1=buf;
#define getchar() (*p1++)
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
const int maxn=500005;
struct Edge {
	int u,v,w,id;
	inline void Read() {
		read(u),read(v),read(w);
	}
}E[maxn];
inline bool cmpw(const Edge &a,const Edge &b) {
	return a.w<b.w;
}
inline bool cmpid(const Edge &a,const Edge &b) {
	return a.id<b.id;
}
int n,m,q,ver[maxn],tot;
namespace LCT {
	struct Node {
		int ch[2],fa,tag,val,pos;
		inline void Clear() {
			ch[0]=ch[1]=fa=tag=val=pos=0;
		}
		inline int& operator [] (const int &x) {
			return this->ch[x];
		}
	}T[maxn<<1];
	inline void Reverse(int x) {
		if(x)
			swap(T[x][0],T[x][1]),T[x].tag^=1;
	}
	inline void Check(int x,int y) {
		if(!y)
			return;
		if(T[T[x].pos].val<T[T[y].pos].val)
			T[x].pos=T[y].pos;
	}
	inline void Pushup(int x) {
		T[x].pos=x,Check(x,T[x][0]),Check(x,T[x][1]);
	}
	inline void Pushdown(int x) {
		if(T[x].tag)
			Reverse(T[x][0]),Reverse(T[x][1]),T[x].tag=0;
	}
	inline int Isroot(int x) {
		return T[T[x].fa][0]!=x&&T[T[x].fa][1]!=x;
	}
	inline int Isright(int x) {
		return T[T[x].fa][1]==x;
	}
	inline void Pushtag(int x) {
		if(!Isroot(x))
			Pushtag(T[x].fa);
		Pushdown(x);
	}
	inline void Rotate(int x) {
		int y=T[x].fa,z=T[y].fa,r=Isright(x),l=r^1;
		if(!Isroot(y))
			T[z][Isright(y)]=x;
		T[x].fa=z,T[y].fa=x,T[T[x][l]].fa=y,T[y][r]=T[x][l],T[x][l]=y,Pushup(y),Pushup(x);
	}
	inline void Splay(int x) {
		Pushtag(x);
		for(int y;!Isroot(x);Rotate(x))
			if(!Isroot(y=T[x].fa))
				Rotate(Isright(x)^Isright(y)?x:y);
	}
	inline void Access(int x) {
		for(int y=0;x;x=T[y=x].fa)
			Splay(x),T[x][1]=y,Pushup(x);
	}
	inline void Makeroot(int x) {
		Access(x),Splay(x),Reverse(x);
	}
	inline void Findpath(int x,int y) {
		Makeroot(x),Access(y),Splay(y);
	}
	inline int Findroot(int x) {
		Access(x),Splay(x);
		for(;T[x][0];x=T[x][0]);
		return Splay(x),x;
	}
	inline void Link(int x,int y) {
		Makeroot(x);
		if(Findroot(y)!=x)
			T[x].fa=y;
	}
	inline void Cut(int x,int y) {
		Makeroot(x);
		if(Findroot(y)!=x)
			return;
		Splay(y);
		if(T[y][0]!=x)
			return;
		T[x].fa=T[y][0]=0,Pushup(y);
	}
}
namespace Dsu {
	int fa[maxn];
	inline void Init() {
		for(int i=1;i<=n;++i)
			fa[i]=i;
	}
	inline int Find(int x) {
		return fa[x]==x?x:fa[x]=Find(fa[x]);
	}
}
int main() {
	fread(buf,1,30<<20,stdin);
	read(n),read(m),Dsu::Init();
	for(int i=1;i<=m;++i)
		E[i].Read(),E[i].id=i,LCT::T[i+n].val=E[i].w;
	sort(E+1,E+m+1,cmpw);
	int cnt=1;
	for(int i=1;cnt<n&&i<=m;++i) {
		int u=E[i].u,v=E[i].v,x=Dsu::Find(u),y=Dsu::Find(v);
		if(x==y)
			continue;
		Dsu::fa[x]=y,LCT::Link(u,E[i].id+n),LCT::Link(v,E[i].id+n),++cnt;
	}
	sort(E+1,E+m+1,cmpid);
	for(read(q);q--;) {
		read(tot);
		for(int i=0;i<tot;++i)
			read(ver[i]);
		int flg=1;
		for(int i=0;i<tot;++i) {
			int u=E[ver[i]].u,v=E[ver[i]].v;
			LCT::Findpath(u,v);
			int p=LCT::T[v].pos,x=p-n;
			if(LCT::T[p].val<E[ver[i]].w) {
				flg=0;
				break;
			}
			LCT::Cut(E[x].u,p),LCT::Cut(E[x].v,p),LCT::T[ver[i]+n].val=0,LCT::Link(u,ver[i]+n),LCT::Link(v,ver[i]+n);
		}
		puts(flg?"YES":"NO");
		for(int i=0;i<tot;++i)
			LCT::T[ver[i]+n].val=E[ver[i]].w,LCT::Splay(ver[i]+n);
	}
}

你可能感兴趣的:(LCT,C++)