1002. [WZOI2011 S3] 周年纪念日

Posted 心之所向 素履以往

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1002. [WZOI2011 S3] 周年纪念日相关的知识,希望对你有一定的参考价值。

                                            1002. [WZOI2011 S3] 周年纪念日

Problem 3 周年纪念日

 (anniversary.pas/c/cpp)



背景

WZland即将迎来一个举国欢庆的日子—建国150亿周年纪念日,值此之际WZland有许多事情要准备……

问题描述

WZland的国王决定举办一个晚会,这次晚会要求所有的WZland居民都参加,但是他发现WZland的所有城市之间的道路都已经毁坏(平时WZland的居民都在自己的城市里活动,所以他们对于那些道路一点都不关心)。这是一件十分麻烦的事情,因为这个晚会要求所有居民都来参加,于是国王决定要重建道路。
他找出了WZland的地图,他发现WZland有N个城市,有M条破败的双向道路连接着这N个城市。由于周年纪念日就要到来,为了节省时间国王决定修建的道路恰好将这N个城市连接起来。修建一条长度为L的道路,需要花费L个Ws(Ws是WZland的通用货币),国王想要将总的花费降到最少,这样他能有足够多的钱来举办晚会。国王还有一个要求,因为这些道路是同时开始修建的,因此修建完这些道路的总时间等于修建完最长的那条道路的时间(你可以认为所有工人的修建速度是一样的,即一单位时间修建1单位长度的道路),国王想要总的修建时间最少。
由于要举行晚会,国王需要找到一个地点来举办晚会,这同样是一件十分麻烦的事情。因为WZland的每个人都十分懒,他们不愿意多走路(就连在这周年纪念日也不例外)。WZland的居民每走1单位长度的路就会产生1单位的不高兴度(这就是为什么他们都不愿离开自己城市的原因)。WZland的国王想要这个晚会的不高兴度尽量低(晚会的不高兴度就等于所有参加晚会的人的不高兴度的和),这就要求选取一个合适的地点来举办晚会(WZland的居民要通过即将修建好的道路,来到这个晚会举办地)。
举个例子,如果晚会在城市u举行,城市i有Pi个人,城市u和城市i的距离为D(u,i),那么这些人产生的不高兴度之和为Pi*D(u,i)。
国王把这个任务交给了你,他希望你能找出一个地点,是所有人的不高兴度之和最小。


输入格式

 

输入数据第一行包含两个整数N和M,表示WZland有N个城市,M条破坏的道路;
第2行到N+1行:第i+1行包含一个整数Pi,表示城市i的居民人数。
第N+2行到N+M+1行,每行三个整数A,B,L(1≤A,B≤N,A≠B),表示A和B之间有一条长度为L的破坏道路。

注意:两个城市之间可能会有多条道路。

 

输出格式

 

输出数据包含两行。
第一行两个整数C,T(用一个空格隔开),表示修建道路的最少花费和最少时间。
第二行两个整数U,H(用一个空格隔开),表示晚会的地点(如果有多个地点满足条件,输出编号最小的那个城市)和相应地点所有人的不高兴度之和。


样例输入输出

 

anniversary.in 
5 7
3
2
2
1
4
1 5 1
1 3 7
2 1 4
2 3 6
3 4 5
2 4 3
5 4 2


anniversary.out
11 5
5 29


样例解释

合法的修建方案如下图(相邻城市之间的距离和每个城市的人数见样例):

技术分享

 

对题第一问 毫无疑问用kurskal就可以了 

1e6的数据 prim是不行的 

对于第二问 把最小生成树分离出来跑 树形dp 

显然不能每个点都跑一次 

我们需要从已知的点去更新其他的点  

所以先从1跑一次dp

son数组 记录 他每个子树有多少节点

d数组记录当前节点的所有子树的节点 跑到这个节点的不高兴值 

rout数组记录 从他父亲节点跑到这个节点的长度 

dp数组 记录以每个节点为根的不高兴值 

 

例如样例 假设我们已经更新到了dp[5] 

拿下一个就是 dp[4] 

dp[5]=people[1]*rout[5]+people[2]*rout[2]+people[3]*rout[3]+(peolpe[2]+people[3]+people[4])*rout[4];

dp[4]=people[1]*rout[5]+people[2]*rout[2]+people[3]*rout[3]+(people[1]+people[5])*rout[4];

显然 两个式子是有重叠部分的 

我们可以看出 当前节点的不高兴值等于

它父亲节点的不高兴值减去当前节点son值到他父亲节点的路径 

再加上剩余人数乘上从父亲节点到当前节点的路径 

 

技术分享
  1 #include <cstdio>
  2 #include <cctype>
  3 #include <algorithm>
  4 
  5 typedef long long LL;
  6 
  7 const LL INF=1e18*5;
  8 const int MAXN=100010;
  9 
 10 int n,m;
 11 
 12 int people[MAXN],fa[MAXN];
 13 
 14 LL total;
 15 
 16 LL d[MAXN],dp[MAXN],son[MAXN],rout[MAXN];
 17 
 18 struct edge {
 19     int x,y;
 20     int val;
 21     friend bool operator < (edge a,edge b) {
 22         return a.val<b.val;
 23     }
 24 };
 25 edge e[MAXN<<1];
 26 
 27 struct node {
 28     int to;
 29     int next;
 30     int val;
 31     node() {}
 32     node(int to,int val,int next):to(to),val(val),next(next) {}
 33 };
 34 node Edge[MAXN<<1];
 35 
 36 int head[MAXN],tot;
 37 
 38 inline void read(int&x) {
 39     int f=1;register char c=getchar();
 40     for(x=0;!isdigit(c);c==-&&(f=-1),c=getchar());
 41     for(;isdigit(c);x=x*10+c-48,c=getchar());
 42     x=x*f;
 43 }
 44 
 45 inline void add(int x,int y,int val) {
 46     Edge[++tot]=node(y,val,head[x]);
 47     head[x]=tot;
 48     Edge[++tot]=node(x,val,head[y]);
 49     head[y]=tot;
 50 }
 51 
 52 inline int find(int x) {
 53     if(x==fa[x]) return x;
 54     return fa[x]=find(fa[x]);
 55 }
 56 
 57 inline void kurskal() {
 58     int cnt=0,mx=-1;
 59     LL sum=0;
 60     for(int i=1;i<=m;++i) {
 61         int xx=find(e[i].x);
 62         int yy=find(e[i].y);
 63         if(xx!=yy) {
 64             add(e[i].x,e[i].y,e[i].val);
 65             fa[xx]=yy;
 66             sum+=(LL)e[i].val;
 67             if(e[i].val>mx) mx=e[i].val;
 68             ++cnt;
 69             if(cnt==n-1) break;
 70         }
 71     }
 72     printf("%lld ",sum);
 73     printf("%d\n",mx);
 74     return;
 75 }
 76 
 77 void DP(int now,int f) {
 78     son[now]=people[now];
 79     for(int i=head[now];i;i=Edge[i].next) {
 80         int v=Edge[i].to;
 81         if(v==f) continue;
 82         DP(v,now);
 83         son[now]+=son[v];
 84         rout[v]=Edge[i].val;
 85         d[now]+=(LL)(d[v]+(LL)son[v]*rout[v]);
 86     }
 87 }
 88 
 89 void DP2(int now,int f) {
 90     dp[now]=dp[f]-(LL)(son[now]*rout[now])+(LL)((total-son[now])*rout[now]);
 91     for(int i=head[now];i;i=Edge[i].next) {
 92         int v=Edge[i].to;
 93         if(v==f) continue;
 94         DP2(v,now);
 95     }
 96 }
 97 
 98 int hh() {
 99     freopen("anniversary.in","r",stdin);
100     freopen("anniversary.out","w",stdout);
101     read(n);read(m);
102     for(int i=1;i<=n;++i) fa[i]=i,read(people[i]),total+=people[i];
103     for(int i=1;i<=m;++i) read(e[i].x),read(e[i].y),read(e[i].val);
104     std::sort(e+1,e+1+m);
105     kurskal();
106     DP(1,0);
107     dp[1]=d[1];
108     for(int i=head[1];i;i=Edge[i].next) {
109         int v=Edge[i].to;
110         DP2(v,1);
111     }
112     LL ans=INF;
113     int pos;
114     for(int i=1;i<=n;++i) 
115       if(dp[i]<ans) ans=dp[i],pos=i;
116     printf("%d %lld\n",pos,ans);
117     return 0;
118 }
119 
120 int sb=hh();
121 int main(int argc,char**argv) {;}
代码

 

以上是关于1002. [WZOI2011 S3] 周年纪念日的主要内容,如果未能解决你的问题,请参考以下文章

cogs——1001. [WZOI2011 S3] 消息传递

cogs 1001. [WZOI2011 S3] 消息传递 Tarjan

BestCoder 1st Anniversary ($) 1002.Hidden String

Android:通过周年纪念日事件获取联系人

微软发文庆祝 .NET 诞生 20 周年纪念日!

我的 CSDN 两周年创作纪念日