可以参考汪伟正和梁旭罡的博客
Problem 1 :奶牛大集会
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号没有奶牛居住。
1 3 4 5
@--1--@--3--@--3--@[2]
[1] |
2
|
@[1]
2
Bessie可以在五个农场中的任意一个举办集会,下面就是在每个位置举办集会的不方便值的统计表。
集会地点 ----- 不方便程度 ------
B1 B2 B3 B4 B5 Total
1 0 3 0 0 14 17
2 3 0 0 0 16 19
3 1 2 0 0 12 15
4 4 5 0 0 6 15
5 7 8 0 0 0 15
如果Bessie在农场1举办集会,那么每个农场各自的不方便值分别是
农场 1 0 -- 到达不需要时间!
农场 2 3 -- 总的距离是 2+1=3 x 1 奶牛 = 3
农场 3 0 -- 没奶牛!
农场 4 0 -- 没奶牛!
农场 5 14 -- 总的距离是 3+3+1=7 x 2 奶牛 = 14
因此,总的不方便值是17。
最小的不方便值是15,当在3号,4号或者5号农场举办集会的时候。
题目名称: gather
输入格式
* 第一行:一个整数N
* 第二到N+1行:第i+1行有一个整数C_i
* 第N+2行到2*N行,第i+N+1行为3个整数:A_i,B_i和L_i。
样例输入(gather.in):
5
1
1
0
0
2
1 3 1
2 3 2
3 4 3
4 5 3
输出格式:
* 第一行:一个值,表示最小的不方便值。
样例输出(gather.out):
1530%的数据n<=20
50%的数据n<=5000
80%的数据n<=50000
100%的数据n<=100000
这道题理解花了很多时间
考试的时候有想到是带权中位数,但是因为是树,而且子树个数不清楚,而带权中位数以前都是用在直线上的,因此没有继续深究。
后来知道了带权中位数也可以应用在树上。
首先一个非常重要的结论,就是带权中位数一定在某一个点上(证明略),因此如果每一个子节点上能够得到更优解就果断移动到这个节点上,最终肯定能够移动到带权中位数那个节点。
随便找一个点(如1)作为根,用一次深搜或者广搜,后序求出所有子树的奶牛数量(包括总奶牛数),并前序求出每个节点到这个根的距离。同时维护出复杂度。然后再进行一次dfs(即为移动的过程),移动到复杂度最小的点。
(我卡在了这一个地方,我担心有环,两次dfs的条件又不同,建出来的树就不同,导致维护的奶牛数量有错。但是我又错了,梁旭罡讲了才明白,因为忘了N个点,N-1条边,每两个点互相连通。这已经时给出了一棵唯一的树了)(经常都是这种担心。。。没由头的,结果仔细看题才发现题弄错了)
(我还卡在了一个地方,就是子树个数不明确,我以为向某个方向移动,要求出代价变化,要计算每一棵子树,时间代价就高了。想了很久恍然大悟,其他的子树中牛的数量用总数减去这棵子树的数量就行了)
我觉得抽象出来就是,一棵子树代表一个方向,左或右,然后其他所有子树就代表另一个方向,左或右。
然后理清思路之后这道题就不难了,代码实现也不复杂。
但是不熟练,以后还要再拿出来做
(另外重要的,类型不同时,必须先进行转换,再进行运算,否则容易超界,另外如long long常数要在末尾加上ll)
AC程序
#include <cstdio>
#include <cstdlib>
#include <iostream>
typedef long long ll;
/* *\
long long不能用printf
才能过
弄懂之后提交次次
第一次 wa70
第二次~第五次 wa70
ans -= (long long)(ths->pow)*(long long)((amount[ths->index]<<1)-amount[1]);
必须先转换再运算,不能先运算再转换,否则超界!!
\* */
struct tnode
{
long index;
tnode* next;
long pow;
};
long n;
long c[100100];
tnode* farm[100100];
long amount[100100];
long long ans = 0;
long long dist[100100];
bool visited[100100];
/*
void bfs()
{
visited[1] = true;
long l=0;long r=0;
que[++r].dist = 0;
que[r].index = 1;
while (l<r)
{
B& now = que[++l];
tnode* ths = farm[now.index];
while (ths)
{
if (!visited[ths->index])
{
que[++r].index = ths->index;
que[r].dist = now.dist+ths->pow;
amount[ths->index] = amount[now.index]+c[ths->index];
ans += amount[ths->index] * que[r].dist;
visited[ths->index] = true;
}
ths = ths->next;
}
}
}
*/
void insert(long a,long b,long c)
{
tnode* nnode = new tnode;
nnode->index = b;
nnode->pow = c;
nnode->next = farm[a];
farm[a] = nnode;
}
void work(long l)
{
tnode* ths = farm[l];
while (ths)
{
if (!visited[ths->index]&&(amount[ths->index]<<1)-amount[1]>0)
{
ans -= (long long)(ths->pow)*(long long)((amount[ths->index]<<1)-amount[1]);
visited[ths->index] = true;
work(ths->index);
visited[ths->index] = false;
}
ths = ths->next;
}
}
/* *\
因为没有环(n-1条边)
所以不管怎样建的树
是唯一的
\* */
long dfs(long l,long long s)
{
tnode* ths=farm[l];
dist[l] = s;
amount[l] = c[l];
ans+=c[l]*s;
while (ths)
{
if (!visited[ths->index])
{
visited[ths->index] = true;
amount[l] += dfs(ths->index,s+ths->pow);
}
ths = ths->next;
}
return amount[l];
}
int main()
{
freopen("gather.in","r",stdin);
freopen("gather.out","w",stdout);
scanf("%ld",&n);
for (long i=1;i<n+1;i++)
scanf("%ld",c+i);
for (long i=1;i<n;i++)
{
long a;long b;long l;
scanf("%ld %ld %ld",&a,&b,&l);
insert(a,b,l);
insert(b,a,l);
}
/* *\
随便找的一
个点作为根
\* */
/*bfs();
for (long i=2;i<n+1;i++)
{
visited[i] = false;
}*/
visited[1] = true;
dfs(1,0);
for (long i=2;i<n+1;i++)
{
visited[i] = false;
}
visited[1] = true;
work(1);
std::cout << ans;
}