巨额奖金 Award

来源:互联网 发布:程序员求职简历怎么写 编辑:程序博客网 时间:2024/04/30 07:38

巨额奖金
Award/.IN/.OUT/.PAS/.EXE
问题描述
NJ 市的快速发展得益于其便捷的交通。可是,随着经济的发展,大量的人进入NJ 市,
NJ 市的交通也承受着巨大的压力。现在,NJ 市正在筹划建设一个新型的交通枢纽,从而
减轻交通的压力。
NJ 市包含n 个区,有些区之间有双向的干道存在。新型交通枢纽建设在这些干道的基
础上,将其中的部分干道改进为新型干道。改进后,干道能承受的压力可以比原来增加几十
倍。为了和谐发展,在新型的交通枢纽建成后,要求任何两个区之间都可以只通过新型干道
(直接或间接地)连接。政府已经预测出每条干道改进为新型干道的费用。政府希望建设新
型交通枢纽的总费用最小,并以巨额奖金向市民征集方案。政府很快发现费用最小的方案不
一定唯一,所以决定将奖金平分给每一种方案的第一个设计者,即如果一个人设计的费用是
最小的而且前面没人和他设计出一模一样的方案,则他可获奖。

栋栋被奖金深深的吸引,准备设计一种方案。可是,他发现方案可能会很多,如果最后
获奖者太多,巨额的资金分到每个人头上的也不会太多。所以他决定先算一下可行的方案数
是多少。
输入
输入的第一行包含两个数n (1 ≤ n ≤ 100),m (1 ≤ m ≤ 1,000),分别表示该市有
多少个区和有多少条干道。接下来m 行,每行三个数ai、bi、ci (1 ≤ ai, bi ≤ n, 1 ≤ ci ≤
1,000,000,000),表示ai 区和bi 区之间有一条干道,如果改进需要ci 的费用。
输入保证任何两个区之间至多有一条干道。对于任何一个费用c,不会有超过10 条干
道的费用都是c。
输出
输出费用最小的方案有多少种。由于答案可能很大,你只要输出方案数除以31011 的
模即可。


具体来说,就是求图的最小生成树个数

做法是先生成一遍最小生成树,记录树中各种权值的边有多少个

然后得出考虑这些边的生成树个数

某论文上有讲基尔霍夫矩阵求生成树个数的方法,十分高端

对于这道题,同一费用边不超过10个

则所以重新做一遍prim,

对于每种权值不同的边进行一遍搜索看有多少种可行解

乘起来以后随意找一种方案合并,继续寻找下一种权值不同的边

证明虽觉屌,但不明

证明:

我们用反证法来证明:假如对于图G的某一个最小生成树,其子图Gi不是一棵树,而是若干个连通块,那么由于图G是连通的,所以我们可以确定Gi中的连通块必然通过若干条其他边连起来。那么此时我们将子图中某条可以将两个连通块连接起来的边加入图中(这条边必定可以找到,因为图G’中,Gi为连通分量),这样图G的最小生成树就会形成一个环,并且这个环上必定有权值比c大的边(因为必定有边将Gi与其他子图连接起来),这时我们去掉权值比c大的边,可以得到一颗新的生成树,而且这颗生成树比最小生成树还要小,这样就产生了矛盾。



program award;type  lin=record        u,v,c:longint;      end;const  maxn=31011;var  can:boolean;  now,ans,count,s,tot,n,m,i,j,k,x,y:longint;  line:array [0..1001] of lin;  cop,father,dl:array [0..101] of longint;procedure swap (var a,b:lin);var  i:lin;begin  i:=a;  a:=b;  b:=i;end;procedure qsort (s,e:longint);var  i,j,k:longint;begin  if s>=e then exit;  i:=s;  j:=e;  k:=line[(s+e) div 2].c;  while i<=j do    begin      while line[i].c<k do inc(i);      while line[j].c>k do dec(j);      if i>j then break;      swap(line[i],line[j]);      inc(i);      dec(j);    end;  qsort(s,j);  qsort(i,e);end;function root (now:longint):longint;begin  if father[now]=0 then exit(now);  father[now]:=root(father[now]);  exit(father[now]);end;function mother (now:longint):longint;begin  if father[now]=0 then exit(now);  exit(mother(father[now]));end;procedure search (k,s,e,left:longint);var  i,x,y:longint;begin  if k>e then    begin      if not can then        begin          cop:=father;          can:=true;        end;      inc(now);      exit;    end;  if e-k+1>left then search(k+1,s,e,left);  if left>0 then    begin      x:=mother(line[k].u);      y:=mother(line[k].v);      if x=y then exit;      father[y]:=x;      search(k+1,s,e,left-1);      father[y]:=0;    end;end;begin  assign(input,'award.in');  reset(input);  assign(output,'award.out');  rewrite(output);  read(n,m);  if m<n-1 then    begin      writeln(0);      close(input);      close(output);      halt;    end;  if m=n-1 then    begin      writeln(1);      close(input);      close(output);      halt;    end;  for i:=1 to m do    read(line[i].u,line[i].v,line[i].c);  qsort(1,m);  tot:=0;  for i:=1 to m do    begin      x:=root(line[i].u);      y:=root(line[i].v);      if x<>y then        begin          inc(tot);          dl[tot]:=line[i].c;          father[x]:=y;        end;      if tot=n-1 then break;    end;  if tot<n-1 then    begin      writeln(0);      close(input);      close(output);      halt;    end;  fillchar(father,sizeof(father),0);  ans:=1;  i:=1;  k:=1;  while i<=m do    begin      while line[i].c<>dl[k] do inc(i);      s:=i;      while (line[i].c=dl[k]) do inc(i);      count:=1;      while (k<tot)and(dl[k+1]=dl[k]) do        begin          inc(count);          inc(k);        end;      inc(k);      now:=0;      can:=false;      search(s,s,i-1,count);      father:=cop;      ans:=(ans*now) mod maxn;      if k>tot then break;    end;  writeln(ans);  close(input);  close(output);end.


原创粉丝点击