1700施工方案第二季{转 仕林}

来源:互联网 发布:如何用sql建立数据库 编辑:程序博客网 时间:2024/04/29 13:03
总的来说,这是一道考图论知识的题,可以搜到很多关于树的直径的东西,以及一些证明,这是一道好题!
programshigong;
type arr=array[1..200000]oflongint;
varn,m,tot,{边数}
   one,{以节点1为树根时dfs得到的最远点}
  two:longint;{以one为树根时dfs得到的最远点,d(one,two)既为树的直径}
   max:int64;{最远的路径}
   x,y,w:array[1..200000]oflongint;{初始边}
  f,{并查集中用作父亲节点,清零后在dfs中用来存每个点的前驱}
   d:array[1..100000]oflongint;{记录每个点到树根的路径长度}
   mstx,msty,mstw:array[1..200000]oflongint;{存最小生成树}
   s,t:array[1..100000]oflongint;{每个节点作为有向边起点时连接的边编号范围}
   v:array[1..100000]ofboolean;{标志数组避免dfs重复访问}

procedureinit;
vari:longint;
begin
 read(n);read(m);
  for i:=1 tom do
   begin
    read(x[i]);read(y[i]);read(w[i]);
   end;
end;

procedureqsort(l,r:longint;var a,b,c:arr);{快排的设计很巧妙}
vari,j,x,y:longint;
begin
 i:=l;j:=r;
 x:=a[(l+r)shr 1];
 repeat
   while a[i]<x doinc(i);
   while a[j]>x dodec(j);
   if i<=j thenbegin
    y:=a[i];a[i]:=a[j];a[j]:=y;
    y:=b[i];b[i]:=b[j];b[j]:=y;
    y:=c[i];c[i]:=c[j];c[j]:=y;
    inc(i);dec(j);
     end;
  untili>j;
  ifi<r then qsort(i,r,a,b,c);
  ifj>l then qsort(l,j,a,b,c);
end;

functionfind(p:longint):longint;
begin
  if f[p]=pthen exit(p);
 f[p]:=find(f[p]);
 exit(f[p]);
end;

procedureunion(a,b:longint);
varx,y:longint;
begin
 x:=find(a);y:=find(b);
 f[x]:=y;
end;

functionsame(a,b:longint):boolean;
begin
  iffind(a)=find(b) then exit(true);
 exit(false);
end;

procedurekruskal;
vari:longint;ans:int64;
begin
 qsort(1,m,w,x,y);{此处按权值排序}
  for i:=1 ton do f[i]:=i;
 ans:=0;tot:=0;
  for i:=1 tom do
  if not same(x[i],y[i]) then
    begin
    inc(ans,w[i]);
    union(x[i],y[i]);
     inc(tot);
    mstx[tot]:=x[i];
    msty[tot]:=y[i];
    mstw[tot]:=w[i];
     if tot=n-1 thenbreak;
    end;
 writeln(ans);
end;

procedureprepare;
vari:longint;
begin
  for i:=1 totot do
 begin
  mstx[n+i-1]:=msty[i];
  msty[n+i-1]:=mstx[i];
  mstw[n+i-1]:=mstw[i];
 end;
 tot:=tot*2;
 qsort(1,tot,mstx,msty,mstw);{注意:此处按始节点排序}
 s[mstx[1]]:=1;
 t[mstx[tot]]:=tot;
  for i:=1 totot do
  begin
   if(i>1)and(mstx[i]<>mstx[i-1])then s[mstx[i]]:=i;
   if(i<tot)and(mstx[i]<>mstx[i+1])then t[mstx[i]]:=i;
  end;
end;

proceduredfs(p:longint);
vari:longint;
begin
 v[p]:=true;
  for i:=s[p]to t[p] do
  if not v[msty[i]] then
   begin
    d[msty[i]]:=d[p]+mstw[i];
     ifmax<d[msty[i]] then
     begintwo:=msty[i];max:=d[msty[i]];end;
    f[msty[i]]:=p;
    dfs(msty[i]);
   end;
end;

functionmin(a,b:longint):longint;
begin
  ifa<b then exit(a);
 exit(b);
end;

functionmaxx(a,b:longint):longint;
begin
  ifa>b then exit(a);
 exit(b);
end;

proceduremain;
varnow,ans:int64;
begin
 fillchar(v,sizeof(v),false);
 max:=0;two:=1;
 dfs(mstx[1]);
 one:=two;max:=0;
 fillchar(d,sizeof(d),0);
 fillchar(v,sizeof(v),false);
 fillchar(f,sizeof(f),0);
 dfs(one);
 now:=0;ans:=maxlongint;
  whiletwo<>one do
  begin
  ans:=min(ans,maxx(now,max-now));{寻找在树的直径上距离两端路径长度的最大   值最小的点,此节点即为所求,根据直径的定义,可以知道在直径之外的点到这     点的路径长<=这个最小的最大值,否则这条就不是直径了,所以此法正解}
   now:=now+d[two]-d[f[two]];
   two:=f[two];
  end;
 write(ans);
end;

begin
 init;
 kruskal;
 prepare;
 main;
end.