BZOJ1827 [Usaco2010 Mar]gather 奶牛大集会
题目描述 Description |
Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会。当然,她会选择最方便的地点来举办这次集会。每个奶牛居住在 N(1<=N<=100,000) 个农场中的一个,这些农场由N-1条道路连接,并且从任意一个农场都能够到达另外一个农场。道路i连接农场A_i和B_i(1 <= A_i <=N; 1 <= B_i <= N),长度为L_i(1 <= L_i <= 1,000)。集会可以在N个农场中的任意一个举行。另外,每个牛棚中居住者C_i(0 <= C_i <= 1,000)只奶牛。在选择集会的地点的时候,Bessie希望最大化方便的程度(也就是最小化不方便程度)。比如选择第X个农场作为集会地点,它的不方便程度是其它牛棚中每只奶牛去参加集会所走的路程之和,(比如,农场i到达农场X的距离是20,那么总路程就是C_i*20)。帮助Bessie找出最方便的地点来举行大集会。 考虑一个由五个农场组成的国家,分别由长度各异的道路连接起来。在所有农场中,3号和4号没有奶牛居住。 |
输入描述 Input Description |
第一行:一个整数N * 第二到N+1行:第i+1行有一个整数C_i * 第N+2行到2*N行,第i+N+1行为3个整数:A_i,B_i和L_i。 |
输出描述 Output Description |
第一行:一个值,表示最小的不方便值。 |
样例输入 Sample Input |
5 1 1 0 0 2 1 3 1 2 3 2 3 4 3 4 5 3 |
样例输出 Sample Output |
15 |
数据范围及提示 Data Size & Hint |
无 |
之前的一些废话:
之前做题都懒得写博客,因此欠了一大笔帐,感觉无比的颓废,从现在起写博客也要变成做题的一部分!然后格式全部更新换代了。
题解:
感觉此题跟找重心类似,然而除了递归的求出了子树的信息后没法快速算出父亲以上组成树的答案,然而我们设ans1[now]表示以now为集市的子树答案,这个可以通过递归处理,ms[now]表示now的子树的点权值和,递推的话是这样ans1[now]+= ans1[now]+=(ans1[son]+ms[son]*w),预处理完了之后我们就可以算出父亲以上组成树的答案了,设ans2[now]表示以now建集市的答案。递推是ans2[now]=ans2[1]+(sum-2*ms[now])*w,然后两次dfs就可以算出结果了。还有一种基于贪心的做法,思想类似,假如now的答案为ans,那么他的儿子答案为ans+(sum-2*ms[now])*w,所以只需要判断sum-2*ms[now]是不是小于0,这样就可以更新最小值了。
代码:(一份是基于贪心的)
#include<iostream> #include<algorithm> #include<cstdio> #include<queue> #include<cmath> #include<cstring> using namespace std; typedef long long LL; typedef pair<int,int> PII; #define mem(a,b) memset(a,b,sizeof(a)) inline int read() { int x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; } const int maxn=100010; struct Edge { int u,v,w,next; Edge() {} Edge(int _1,int _2,int _3,int _4):u(_1),v(_2),w(_3),next(_4) {} }e[maxn<<1]; int n,ce=-1,a,b,c,first[maxn],A[maxn]; LL ms[maxn],ans1[maxn],ans2[maxn],ans=(LL)1<<50,sum; void addEdge(int a,int b,int c) { e[++ce]=Edge(a,b,c,first[a]);first[a]=ce; e[++ce]=Edge(b,a,c,first[b]);first[b]=ce; } void dfs(int now,int fa) { ms[now]=A[now]; for(int i=first[now];i!=-1;i=e[i].next) if(e[i].v!=fa) { dfs(e[i].v,now); ms[now]+=ms[e[i].v]; ans1[now]+=(ans1[e[i].v]+ms[e[i].v]*(LL)e[i].w); } } void rdfs(int now,int fa,int w) { if(!fa)ans2[now]=ans1[now]; else ans2[now]=ans2[fa]+(sum-2ll*ms[now])*(LL)w; for(int i=first[now];i!=-1;i=e[i].next) if(e[i].v!=fa)rdfs(e[i].v,now,e[i].w); } int main() { mem(first,-1); n=read(); for(int i=1;i<=n;i++)A[i]=read(),sum+=A[i]; for(int i=1;i<n;i++)a=read(),b=read(),c=read(),addEdge(a,b,c); dfs(1,0);rdfs(1,0,0); for(int i=1;i<=n;i++)ans=min(ans,ans2[i]); printf("%lld\n",ans); return 0; }
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<queue> 5 #include<cmath> 6 #include<cstring> 7 using namespace std; 8 typedef long long LL; 9 typedef pair<int,int> PII; 10 #define mem(a,b) memset(a,b,sizeof(a)) 11 inline int read() 12 { 13 int x=0,f=1;char c=getchar(); 14 while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} 15 while(isdigit(c)){x=x*10+c-'0';c=getchar();} 16 return x*f; 17 } 18 const int maxn=100010; 19 struct Edge 20 { 21 int u,v,w,next; 22 Edge() {} 23 Edge(int _1,int _2,int _3,int _4):u(_1),v(_2),w(_3),next(_4) {} 24 }e[maxn<<1]; 25 int n,ce=-1,a,b,c,first[maxn],A[maxn]; 26 LL ms[maxn],ans1[maxn],ans,sum; 27 void addEdge(int a,int b,int c) 28 { 29 e[++ce]=Edge(a,b,c,first[a]);first[a]=ce; 30 e[++ce]=Edge(b,a,c,first[b]);first[b]=ce; 31 } 32 void dfs(int now,int fa) 33 { 34 ms[now]=A[now]; 35 for(int i=first[now];i!=-1;i=e[i].next) 36 if(e[i].v!=fa) 37 { 38 dfs(e[i].v,now); 39 ms[now]+=ms[e[i].v]; 40 ans1[now]+=(ans1[e[i].v]+ms[e[i].v]*(LL)e[i].w); 41 } 42 } 43 void rdfs(int now,int fa) 44 { 45 for(int i=first[now];i!=-1;i=e[i].next) 46 if(e[i].v!=fa) 47 if(sum-2*ms[e[i].v]<0) 48 { 49 ans+=(sum-2*ms[e[i].v])*e[i].w; 50 rdfs(e[i].v,now); 51 } 52 } 53 int main() 54 { 55 mem(first,-1); 56 n=read(); 57 for(int i=1;i<=n;i++)A[i]=read(),sum+=A[i]; 58 for(int i=1;i<n;i++)a=read(),b=read(),c=read(),addEdge(a,b,c); 59 dfs(1,0);ans=ans1[1];rdfs(1,0); 60 printf("%lld\n",ans); 61 return 0; 62 }
总结:两次dfs可以弥补一些递归处理没法解决的问题。