Wikioi 1188 city战争(并查集维护)

1188 City战争
时间限制: 1 s
空间限制: 128000 KB
题目等级 : 钻石 Diamond
题解
题目描述 Description
Cyz在你的帮助下很快解决了上个问题,然后好学的Cyz在网上比赛时遇到了一个难题题目的名称叫做City战争,当时Cyz没能解决,赛后Cyz来和你讨论这个问题,希望你能和他一起解决。

问题化简后是这样的:给出N个节点的一棵带边权的树,这棵树上一共有n*(n-1)条不同的路径。Ps:a–>b和b–>a算不同的路径。一条路径上一定有边权最大的边(可能最大边不止一条),树上的每条边都有一个重要度,重要度K表示这条边是K条路径上的最大边。

问题是让你找出树中所有边的最大重要度和最大重要度的边有多少条,并且升序输出这些边的序号。

输入描述 Input Description
第一行,一个整数N,表示一颗树有N个节点,接下来N-1行,每行三个整数a,b,c表示节点a和节点b之间有条权值为c的边

输出描述 Output Description
第一行有两个空格隔开的整数Max和tot,表示树中边的最大的重要度和满足最大重要度的边有tot条。

第二行输出空格隔开的tot个正整数,即这些边的序号。

样例=====1

2
2 1 5
输出
2 1

1

样例=====2

6
1 2 1
1 3 5
3 4 2
3 5 3
3 6 4
输出
16 1

2

样例=====3

4

1 2 1

2 3 3

3 4 4
输出
6 1

3

=====样例4
4

1 2 1

2 3 1

2 4 1
输出
6 3

1 2 3

样例输出 Sample Output
见上

数据范围及提示 Data Size & Hint
【数据规模】

20%的数据满足N<=1000

另有20%的数据满足 C的值都不相同

100%的数据满足 1<=N<=100000, 1<=a,b<=n,C<=Maxlongint

注:务必理解题意

1.先dfs出一棵有根树,则每条边连接父亲和ta的一个儿子
2.把树置空,接着从小到大加边,边权相同的边一起加
3.用冰茶几维护每个点当前所属子树的根,在根处记录子树的节点数
4.记加边后边所属子树的节点数为SumA,加边前以儿子为根的子树节点数为SumB (好绕啊…)
则这条边的重要度为(SumA-SumB)*SumB*2 //注:此式子类似联合权值,将边两边的点的数量相乘,得到经过这条边的路径数量(题目原因,所以乘2)

program df;
type point=^node;
node=record
date,ends:longint;
next:point;
end;
var i,j,n,m,x,y,z,k,t:longint;
s1,s2:int64;
path:array[0..100000] of point;
a,c,f,g,h,aa:array[0..100000] of longint;
s,d,e:array[0..100000] of int64;
b:array[0..100000] of boolean;
procedure com(x,y,z:longint);
var i:point;
begin
i:=path[x];
new(path[x]);
path[x]^.ends:=y;
path[x]^.date:=z;
path[x]^.next:=i;
end;

procedure dfs(x:longint);
var i:point;
y:longint;
begin
i:=path[x];
b[x]:=true;
while i<>nil do
begin
y:=i^.ends;
if not b[y] then
begin
dfs(y);
f[y]:=x; //放前放后都一样,表在意细节
end;
i:=i^.next;
end;
end;

procedure sq(l,r:longint);
var i,j,mm,dd:longint;
begin
i:=l; j:=r;
mm:=e[(l+r) div 2];
repeat
while e[i]小于mm do inc(i);
while e[j]>mm do dec(j);
if i<=j then
begin
dd:=a[i]; a[i]:=a[j]; a[j]:=dd;
dd:=c[i]; c[i]:=c[j]; c[j]:=dd;
dd:=e[i]; e[i]:=e[j]; e[j]:=dd;
dd:=h[i]; h[i]:=h[j]; h[j]:=dd;
inc(i); dec(j);
end;
until i>j;
if j>l then sq(l,j);
if r>i then sq(i,r);
end;

function min(x,y:longint):longint;
begin
if y>x then exit(x)
else exit(y);
end;

function max(x,y:longint):longint;
begin
if x>y then exit(x)
else exit(y);
end;

procedure deal(x,y:longint);
var i:point;
begin
while g[x]<>x do
begin
d[x]:=d[x]+y;
x:=g[x];
end;
d[x]:=d[x]+y;
end;

function find(x:longint):longint;
begin
if g[x]<>x then g[x]:=find(g[x]);
find:=g[x];
end;

begin
readln(n);
for i:=1 to n-1 do
begin
readln(a[i],c[i],e[i]);
com(a[i],c[i],e[i]);
com(c[i],a[i],e[i]);
f[i]:=i; h[i]:=i; //h记录原位置(好吧其实很zz)
end;
dfs(1); //第一步遍历有根树
sq(1,n-1);
for i:=1 to n do
begin
if f[c[i]]<>a[i] then
begin
x:=a[i]; a[i]:=c[i]; c[i]:=x;
end; //保证啊a[i]是c[i]的父节点
g[i]:=i;
d[i]:=1; //记录节点数量
end;
i:=1;
while i<=n-1 do
begin
j:=i;
while (j+1<=n-1) and (e[j+1]=e[i]) do inc(j); //相同权值的个数
for z:=i to j do
begin
g[c[z]]:=a[z];
deal(a[z],d[c[z]]);
end;
for z:=i to j do
s[h[z]]:=d[c[z]]*(d[find(a[z])]-d[c[z]])*2; //(d[find(a[z])]-d[c[z]])指的是未并这条边之前的节点数量,即这条边左边点的数量和右边点的数量相乘
i:=j+1;
end;
s1:=0; t:=0;
for i:=1 to n-1 do
if s[i]>s1 then begin s1:=s[i]; t:=1; aa[t]:=i; end
else if s1=s[i] then begin inc(t); aa[t]:=i; end;
writeln(s1,’ ‘,t);
for i:=1 to t do
write(aa[i],’ ‘);
end.

转载于:https://www.cnblogs.com/Gxyhqzt/p/7784291.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值