本篇文章只介绍在该题被卡常的在线算法LCT,不介绍正常的std做法,如果想学习正常做法的,请出门右转…
给定一张 n ( n ≤ 500000 ) n(n\le 500000) n(n≤500000)个点 m ( m ≤ 500000 ) m(m\le 500000) m(m≤500000)条边的无向图,一共有 q ( q ≤ 500000 ) q(q\le 500000) q(q≤500000)次询问,每次询问给出一个边集,边集大小为 k ( ∑ k ≤ 500000 ) k(\sum k\le500000) k(∑k≤500000),问这个边集里的边是否都能出现在这张图的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才能通过此题。
#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);
}
}