NKOJ3855 merlin
时间限制 : - MS 空间限制 : 165536 KB
评测说明 : 2s
问题描述
古月族是一个神奇的种族,有一天古月腾和古月豪在一起看《梅林传奇(merlin)》。
剧中,以卡梅洛特(camelot)为代表的是古时英格兰的城池坐落在现我大英帝国的某处土地上,每个城池被乌瑟王(亚瑟王之父)编号,方便货物来往。
假设有n座城池,编号为1-n,一些城池之间有单向道连接(逆向行驶会被视为私自入境),共k条。如果有一些城池能够互相到达交通无碍,他们会结为盟友,于是大英帝国上出现了许多帮派,他们要么互相挑起战争,要么选择井水不犯河水,形势极其尴尬,甚至,他们把连到其他帮派外的单向边拆毁了。
现在,乌瑟王死后,亚瑟王想要统一大英,他决定让魔法师梅林在一些城池之间修一些双向边,这样能使所有城池互相联系,交流,促进和平发展。梅林为了帮助亚瑟王,他把可能会修的道路都进行了魔法加特,使走上这条路的人心平气和,向往和平。虽然梅林魔法强大,但他也需要休息,他只能在指定的城池之间建立m条双向边,并且每条双向边上的有一个魔力需求值wi。
梅林已经累趴下了,谁叫亚瑟王这么折磨他(可怜的梅子),他想知道;
怎样连边,才能让所有城池能够互相到达,并且他耗费的魔力最少,求出这个最小值。
是否有不同种建边的方法。
输入格式
多组测试数据。
第一行一个正整数T,对于每一组测试数据:
第一行,三个个整数n,k,m。
接下来k行,每行两个整数,x,y,表示从x城池到y城池有一条单向道路。
接下来m行,每行三个整数,x,y,z,表示可以在城池x和城池y之间,建立一条双向道路,消耗魔法值为z。
输出格式
对于每一组测试数据:
第一行,一个整数表示消耗最少魔力。
第二行,若有不同的建边方法,输出“YES”,否则输出“NO”(均不含引号)。
样例输入 1
1
5 5 5
1 2
2 3
3 1
3 4
4 5
1 2 10
4 5 9
5 1 3
3 4 6
2 4 1
样例输出 1
4
NO
样例输入 2
1
5 5 5
1 2
2 3
3 4
4 5
5 1
1 2 100
2 3 123
4 3 1
1 2 1
2 3 1
样例输出 2
0
NO
样例输入 3
1
5 6 3
1 2
2 1
1 3
3 4
4 5
5 3
1 3 5
2 4 5
3 5 999
样例输出 3
5
YES
提示
【数据范围】
对于30%的数据, 1<=n<=1000,1<=T<=5。
对于100%的数据,1<=n<=500000,1<=m<=100000,1<=k<=1000000 ,1<=x<=n,1<=y<=n,0<=z<=10000,1<=T<=5。
保证m条双向边能够使所有城池交流畅通无阻。
注:有兴趣的同学可以思考,如果每个帮派不把连接到外帮派的单向边拆毁,应该怎么做。(本次考试请根据题目描述做题)。
来源 ht hjh
第一问:强连通分量缩点-> 最小生成树
第二问:
法一:依次加边、减掉环中除加入边外最大边(lca倍增讨论)->生成树大小增加否
#include
#include
#include
#include
using namespace std;
const int needn=500003;
const int needm=100003;
const int needk=1000003;
const double lg2=log(2);
int n,k,m,mm;
//...........................................................
inline void in_(int & d)
{
char t=getchar();
while(t<'0'||t>'9') t=getchar();
for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';
}
//...........................................................
int tot,fi[needn],la[needk],en[needk],len[needk];
bool vis[needn];
void add(int a,int b,int c=0)
{
tot++;
la[tot]=fi[a];
fi[a]=tot;
en[tot]=b;
len[tot]=c;
}
//...........................................................
int visittime,dfn[needn],low[needn],scc,id[needn];
int top,ss[needn];
bool ins[needn];
void tarjan(int x)
{
low[x]=dfn[x]=++visittime;
ss[++top]=x,ins[x]=true;
for(int t=fi[x],y;t;t=la[t])
{
y=en[t];
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(ins[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x])
{
int y;
scc++;
do{
y=ss[top],top--;
ins[y]=false;
id[y]=scc;
}while(y!=x);
}
}
//...........................................................
struct fy
{
int a,b,len;
bool use;
bool operator< (const fy& b) const
{
return len//...........................................................
int be[needn];
int getbe(int x)
{
return x==be[x] ?x :be[x]=getbe(be[x]);
}
void clean()
{
for(int i=1;i<=scc;i++) be[i]=i;
}
//...........................................................
int ans1;
int dm;
int dep[needn],mdis[needn][22],fa[needn][22];
void build_lca(int x)
{
vis[x]=true;
int k=log(dep[x])/lg2;
dm=max(dm,k);
for(int i=1;i<=k;i++)
fa[x][i]=fa[fa[x][i-1]][i-1],mdis[x][i]=max(mdis[x][i-1],mdis[fa[x][i-1]][i-1]);
for(int t=fi[x],y;t;t=la[t])
{
y=en[t];
if(vis[y]) continue;
dep[y]=dep[x]+1;
fa[y][0]=x;
mdis[y][0]=len[t];
build_lca(y);
}
}
bool lca(int k)
{
int a=w[k].a,b=w[k].b;
if(dep[a]>dep[b]) swap(a,b);
int ans=0,c;
for(int i=0,p=dep[b]-dep[a];i<=dm;i++) if((p>>i)&1) ans=max(ans,mdis[b][i]),b=fa[b][i];
if(a==b) c=a;
else
{
for(int i=dm;i>=0;i--)
if(fa[a][i]!=fa[b][i])
{
ans=max(ans,mdis[a][i]),ans=max(ans,mdis[b][i]);
b=fa[b][i],a=fa[a][i];
}
c=fa[a][0];
ans=max(ans,mdis[a][0]),ans=max(ans,mdis[b][0]);
}
if(ans!=w[k].len) return false;
return true;
}
//...........................................................
void ini()
{
ans1=tot=0;
scc=visittime=top=0;
for(int i=1;i<=n;i++) vis[i]=fi[i]=dfn[i]=low[i]=id[i]=ins[n]=0;
for(int i=1;i<=m;i++) w[i].use=false;
for(int i=0,j;i<=dm;i++) for(j=1;j<=n;j++) mdis[j][i]=fa[j][i]=0;
}
//...........................................................
int main()
{
int t;scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&k,&m);
ini();
for(int i=1,a,b;i<=k;i++)
{
in_(a),in_(b);
add(a,b);
}
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
mm=0;
for(int i=1,a,b,c;i<=m;i++)
{
in_(a),in_(b),in_(c);
if(id[a]==id[b]) continue;
mm++;
w[mm].a=id[a],w[mm].b=id[b],w[mm].len=c;
}
sort(w+1,w+1+mm);
clean();
for(int i=1;i<=scc;i++) fi[i]=0;
for(int i=1,x,y,cnt=0;i<=mm&&cnt+1if(x!=y)
{
be[x]=y;
ans1+=w[i].len;
w[i].use=true;
cnt++;
add(w[i].a,w[i].b,w[i].len),add(w[i].b,w[i].a,w[i].len);
}
}
build_lca(1);
bool getans2=false;
for(int i=1;i<=mm;i++)
{
if(!w[i].use&&lca(i))
{
getans2=true;
break;
}
}
printf("%d\n",ans1);
puts(getans2 ? "YES":"NO");
}
}
法二:找出最小生成树后将所有边重新排序,使新的顺序为原来顺序中长度相等的元素反序,重新找最小生成树,如果新加入的边为原来未讨论过的,则YES。
struct fy
{
int a,b,len,id;
bool use;
};
bool way1 (const fy& x,const fy& y)
{
if(x.len==y.len) return x.idreturn x.lenlen;
}
bool way2 (const fy& x,const fy& y)
{
if(x.len==y.len) return x.id>y.id;
return x.lenlen;
}
........
int ans1=0;
sort(w+1,w+1+mm,way1);
clean();
for(int i=1,x,y,cnt=0;i<=mm&&cnt+1if(x!=y)
{
be[x]=y;
ans1+=w[i].len;
w[i].use=true;
cnt++;
add(w[i].a,w[i].b,w[i].len),add(w[i].b,w[i].a,w[i].len);
}
}
bool getans2=false;
sort(w+1,w+1+mm,way2);
clean();
for(int i=1,x,y,cnt=0;i<=mm&&cnt+1if(x!=y)
{
if(!w[i].use)
{
getans2=true;
break;
}
be[x]=y;
w[i].use=true;
cnt++;
add(w[i].a,w[i].b,w[i].len),add(w[i].b,w[i].a,w[i].len);
}
}
printf("%d\n",ans1);
puts(getans2 ? "YES":"NO");