一、题目
二、解法
很容易看出来是 l c t lct lct吧,我们先随便搞一个最小生成树。
询问时尝试加入给的边,如果给定的边已经在最小生成树中是可以的。然后判断连成的环中最大边权是否等于这条边,那我们就删去环上最大的边,然后加入这条边,把这条边打上不可替换标记,也就是把权值赋值为 − 1 -1 −1,最后再改回去。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn),但是会 T T T,贴个代码吧,正解在下面 q w q qwq qwq
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 1000005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,par[M],ch[M][2],val[M],Max[M],fl[M],st[M];
bool use[M];int q,a[M],b[M],c[M],d[M],id[M];
int nrt(int x)
{
return ch[par[x]][0]==x || ch[par[x]][1]==x;
}
int chk(int x)
{
return ch[par[x]][1]==x;
}
void push_up(int x)
{
if(!x) return ;
Max[x]=max(val[x],max(Max[ch[x][0]],Max[ch[x][1]]));
if(Max[x]==val[x]) id[x]=x;
if(Max[x]==Max[ch[x][0]]) id[x]=id[ch[x][0]];
if(Max[x]==Max[ch[x][1]]) id[x]=id[ch[x][1]];
}
void flip(int x)
{
if(!x) return ;
swap(ch[x][0],ch[x][1]);
fl[x]^=1;
}
void push_down(int x)
{
if(!x) return ;
if(fl[x])
{
flip(ch[x][0]);flip(ch[x][1]);
fl[x]=0;
}
}
void rotate(int x)
{
int y=par[x],z=par[y],k=chk(x),w=ch[x][k^1];
ch[y][k]=w;par[w]=y;
if(nrt(y)) ch[z][chk(y)]=x;par[x]=z;
ch[x][k^1]=y;par[y]=x;
push_up(y);push_up(x);
}
void splay(int x)
{
int y=x,z=0;
st[++z]=y;
while(nrt(y)) st[++z]=y=par[y];
while(z) push_down(st[z--]);
while(nrt(x))
{
int y=par[x],z=par[y];
if(nrt(y))
{
if(chk(x)==chk(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
}
void access(int x)
{
for(int y=0;x;x=par[y=x])
splay(x),ch[x][1]=y,push_up(x);
}
void makeroot(int x)
{
access(x);splay(x);
flip(x);
}
int findroot(int x)
{
access(x);splay(x);
while(ch[x][0]) push_down(x),x=ch[x][0];
splay(x);
return x;
}
void split(int x,int y)
{
makeroot(x);
access(y);splay(y);
}
void link(int x,int y)
{
makeroot(x);
if(findroot(y)!=x) par[x]=y;
}
void cut(int x,int y)
{
makeroot(x);
if(findroot(y)==x && par[y]==x && !ch[y][0])
{
par[y]=ch[x][1]=0;
push_up(x);
}
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;i++)
{
a[i]=read()+m;b[i]=read()+m;c[i]=val[i]=read();
push_up(i);
if(findroot(a[i])^findroot(b[i]))
link(a[i],i),link(b[i],i),use[i]=1;
else
{
split(a[i],b[i]);
if(Max[b[i]]<=val[i]) continue;
int t=id[b[i]];//树的形态会改变,存下来
cut(a[t],t);
cut(b[t],t);
link(a[i],i);link(i,b[i]);
use[t]=0;use[i]=1;
}
}
q=read();
while(q--)
{
int k=read(),flag=0;
for(int j=1;j<=k;j++)
{
int i=read();d[j]=i;
if(use[i])
{
makeroot(i);
val[i]=-1;
push_up(i);
continue;
}
split(a[i],b[i]);
if(Max[b[i]]==val[i])
{
int t=id[b[i]];
cut(a[t],t);
cut(b[t],t);
link(a[i],i);link(i,b[i]);
use[t]=0;use[i]=1;
makeroot(i);
val[i]=-1;
push_up(i);
continue;
}
flag=j;
break;
}
if(!flag) puts("YES"),flag=k+1;
else puts("NO");
for(int j=1;j<flag;j++)
{
makeroot(d[j]);
val[d[j]]=c[d[j]];
push_up(d[j]);
}
for(int j=flag+1;j<=k;j++)
read();
}
}
正解需要对 K r u s k a l Kruskal Kruskal 算法有更深的一些理解,首先由这样一些结论:
- 对于任意权值,最小生成树中这种权值的边数是一定的
- 对于任意正确加边方案,加到某一种权值图的连通性是一定的
综合上面两种结论,我们可以推知判断某些边是否能同时出现在最小生成树中,不同权值的边是不会相互影响的,所以这告诉我们可以分权值讨论。
我们先建一颗最小生成树,记录下每条边连接的两个并查集的根,询问的时候先把权值排序,相同权值的放在一起做,这里我们需要连接预处理存下来的两个并查集的根(因为我们需要在以前的基础上做),判断能否连接,如果构成了环就说明不满足条件。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn),但是要比 l c t lct lct快多了,好写多了。
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int M = 500005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,q,p[M];
struct edge
{
int u,v,c,id,tx,ty;
bool operator < (const edge &B) const
{
return c<B.c;
}
}e[M];
void make()
{
for(int i=1;i<=n;i++) p[i]=i;
}
int find(int x)
{
if(p[x]^x) p[x]=find(p[x]);
return p[x];
}
void merge(int x,int y)
{
p[find(x)]=find(y);
}
bool cmp(edge x,edge y)
{
return x.id<y.id;
}
int main()
{
n=read();m=read();
make();
for(int i=1;i<=m;i++)
{
e[i].u=read();e[i].v=read();e[i].c=read();
e[i].id=i;
}
sort(e+1,e+1+m);
for(int i=1;i<=m;)
{
int j=i;
for(;e[i].c==e[j].c;j++)
{
e[j].tx=find(e[j].u);
e[j].ty=find(e[j].v);
}
for(;i<j;i++)
{
if(find(e[i].u)^find(e[i].v))
merge(e[i].u,e[i].v);
}
}
q=read();
sort(e+1,e+1+m,cmp);
make();
while(q--)
{
int k=read();
vector<edge> g;
for(int i=1;i<=k;i++)
{
int x=read();
g.push_back(edge{e[x].tx,e[x].ty,e[x].c,0,0,0});
}
sort(g.begin(),g.end());
bool flag=1;
for(int i=0;i<k && flag;)
{
int j=i;
while(j<k && g[j].c==g[i].c)
{
if(find(g[j].u)==find(g[j].v))
{
flag=0;
break;
}
merge(g[j].u,g[j].v);
j++;
}
for(;i<j;i++)
p[g[i].u]=g[i].u,p[g[i].v]=g[i].v;
}
if(flag) puts("YES");
else puts("NO");
}
}