原题链接:http://codeforces.com/problemset/problem/891/C
题目大意:给出n个点,m条边的图,(0<n,m<10^5),询问多次,每次询问给出一个边集,问是否存在一颗最小生成树包括给出的边集里的所有边。询问的边集的总大小不超过10^5。
题解:把所有边按照边权从小到大排序后,假设每次询问的边集大小都是1,那么只要知道比这条边权值小的所有边构成的边集里,由kruskal选出的边集是否会和这条边形成环。如果不会形成环,那么就是说如果选这条边而后产生的最小生成树不会比原来的最优解更差,那么如果扩展到边集大于1情况,也就是只要使得选上这个边集里的所有边都满足这个条件且内部没有环,似乎就可以判断出结果了。
但是,这样存在着反例,比如(1,2,4),(2,3,4),(3,4,3),(4,1,1)这样的边集,显然第一条边和第二条边不可能同时存在最小生成树里。那么就需要考虑同一个询问里同价值的边是否能同时存在于最小生成树里。也就是假设从小到大枚举到边权为v的边的时候,对于权值小于v的边由kruskal选出的边集,把同一个询问里的价值为v的边全部加入,如果不会形成环则说明至少这一部分是符合要求的,而某个边集符合要求的充要条件就是边集的每个部分都是符合条件的。
最后细节部分用并查集查询是否有环,按照边权排序来依次生成部分的最小生成树。
还是看代码吧:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline void read(int &x){
char ch;
bool flag=false;
for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
x=flag?-x:x;
}
inline void read(long long &x){
char ch;
bool flag=false;
for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
x=flag?-x:x;
}
inline void write(int x){
static const int maxlen=100;
static char s[maxlen];
if (x<0) { putchar('-'); x=-x;}
if(!x){ putchar('0'); return; }
int len=0; for(;x;x/=10) s[len++]=x % 10+'0';
for(int i=len-1;i>=0;--i) putchar(s[i]);
}
const int MAXN = 600010;
const int MAXM = 600010;
struct Edge{
int st,ed,v;
}edge[ MAXN ];
int n , m , q;
vector<int> v_edge[ MAXN ];
vector< pair <int ,int > > v_que[ MAXN ];
int fa[ MAXN ],fa_now[ MAXN ];
int T_cnt[ MAXN ] , T;
bool ans[ MAXN ];
int find(int x){
return fa[x]?fa[x]=find(fa[x]):x;
}
int find_now(int x){
//printf("%d\n",x);
if ( T_cnt[x]!=T ) fa_now[x]=fa[x],T_cnt[x]=T;
return fa_now[x]?fa_now[x]=find_now(fa_now[x]):x;
}
int main(){
read(n);read(m);
for (int i = 1;i<=m;i++)
{
int a,b,c;
read(a);read(b);read(c);
edge[i].st=a; edge[i].ed=b; edge[i].v=c;
v_edge[c].push_back(i);
}
int q ;
read(q);
for (int i = 1;i<=q;i++)
{
int t;
read(t);
for (int j = 1; j<= t ;j++)
{
int x;
read(x);
v_que[ edge[x].v ].push_back( make_pair(i,x ) );
}
}
T=0;
for (int i = 1;i<=500000;i++)
{
for (int j = 0 ; j<v_que[i].size() ;j++)
{
int id_que=v_que[i][j].first;
int id_edge=v_que[i][j].second;
if ( j == 0 || id_que!=v_que[i][j-1].first )
T++;
int x=find_now( edge[ id_edge ].st ), y = find_now ( edge[ id_edge ].ed );
if( x==y )
ans[ id_que ] = 1;
else
fa_now[ x ] = y ;
}
for (int j = 0; j < v_edge[i].size() ; j++)
{
int id_edge=v_edge[i][j];
int x=find( edge[ id_edge ].st ), y= find( edge[ id_edge ].ed );
if ( x!=y )
fa[x]=y;
}
}
for (int i = 1;i<=q;i++)
if ( ans[i] )
puts("NO");
else
puts("YES");
return 0;
}