洛谷·bzoj·伟大的奶牛聚集Great Cow Gather

初见安~~这里是传送门:洛谷P2986 & bzoj P1827

题目描述

Bessie is planning the annual Great Cow Gathering for cows all across the country and, of course, she would like to choose the most convenient location for the gathering to take place.

Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会。当然,她会选择最方便的地点来举办这次集会。

Each cow lives in one of N (1 <= N <= 100,000) different barns (conveniently numbered 1..N) which are connected by N-1 roads in such a way that it is possible to get from any barn to any other barn via the roads. Road i connects barns A_i and B_i (1 <= A_i <= N; 1 <= B_i <= N) and has length L_i (1 <= L_i <= 1,000). The Great Cow Gathering can be held at any one of these N barns. Moreover, barn i has C_i (0 <= C_i <= 1,000) cows living in it.

每个奶牛居住在 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)只奶牛。

When choosing the barn in which to hold the Cow Gathering, Bessie wishes to maximize the convenience (which is to say minimize the inconvenience) of the chosen location. The inconvenience of choosing barn X for the gathering is the sum of the distances all of the cows need to travel to reach barn X (i.e., if the distance from barn i to barn X is 20, then the travel distance is C_i*20). Help Bessie choose the most convenient location for the Great Cow Gathering.

在选择集会的地点的时候,Bessie希望最大化方便的程度(也就是最小化不方便程度)。比如选择第X个农场作为集会地点,它的不方便程度是其它牛棚中每只奶牛去参加集会所走的路程之和,(比如,农场i到达农场X的距离是20,那么总路程就是C_i*20)。帮助Bessie找出最方便的地点来举行大集会。

Consider a country with five barns with [various capacities] connected by various roads of varying lengths. In this set of barns, neither barn 3 nor barn 4 houses any cows.

1 3 4 5

@--1--@--3--@--3--@[2]

[1] |

2 | @[1] 2 Bessie can hold the Gathering in any of five barns; here is the table of inconveniences calculated for each possible location:

Gather ----- Inconvenience ------

Location 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

If Bessie holds the gathering in barn 1, then the inconveniences from each barn are:

Barn 1 0 -- no travel time there!

Barn 2 3 -- total travel distance is 2+1=3 x 1 cow = 3 Barn 3 0 -- no cows there!

Barn 4 0 -- no cows there!

Barn 5 14 -- total travel distance is 3+3+1=7 x 2 cows = 14 So the total inconvenience is 17.

The best possible convenience is 15, achievable at by holding the Gathering at barns 3, 4, or 5.

输入格式:

* Line 1: A single integer: N

* Lines 2..N+1: Line i+1 contains a single integer: C_i

* Lines N+2..2*N: Line i+N+1 contains three integers: A_i, B_i, and L_i

第一行:一个整数 N 。

第二到 N+1 行:第 i+1 行有一个整数 C_i

第 N+2 行到 2*N 行:第 i+N+1 行为 3 个整数:A_i,B_i 和 L_i。

输出格式:

* Line 1: The minimum inconvenience possible

第一行:一个值,表示最小的不方便值。

输入样例#1: 

5 
1 
1 
0 
0 
2 
1 3 1 
2 3 2 
3 4 3 
4 5 3 

输出样例#1: 

15

 

题解:

首先很不正经地吐槽——你谷出数据的人是有多人才?我ans取较小者初始化为999999999999都不行,你的输出能高达14位数?卡这种细节有意思?

好了进入正题。本人用的是换根法,O(n)预处理出以每个点为根需要的距离。这个距离可以在换根法的扶持下借用先前的结果得到。

如果你还是很懵,那么就继续看具体实现。【我的思路是没问题的,也有代码量比我少很多的换根法代码但是时间和空间都没有我的优~】

我们看看样例吧:

【假设以点1为根节点作图】

如果在点1集合,那么就是在节点2和5的奶牛往上走;如果是在节点4集合,就是在节点1的奶牛下来,节点2的奶牛先上再下,节点5的奶牛上来……是的,我的思路就是:每个点都处理处以其为根节点的子树信息和从上方过来的节点的信息。这里的从上方过来,在我的定一下如图,2号点对于4和5来说都算是从上方走过来的。也就是非其子树非其本身的所有点。

因为分成了两部分,所以我们的数组都要开成两维的。一维为上方,一维为下方。

很容易想到的,要算出不方便值,我们肯定要存size。这里我们定义size[u][0]为点u上方所有点的牛的数量,size[u][1]为其子树所有牛的数量。以及不方便值——定义far[u][0]为从上方到达点u的所有点的不方便值,far[u][1]为u的子树的所有点到达u的不方便值。可以的话这两个量初始化的过程就可以自己去思考了。

需要详细解说的话就继续——首先far的求解一定需要size的支持。关于size[u][1]相信你是一定没有问题的——直接回溯时统计子树的size大小即可。size[u][0]是一个难点【debug它卡了我好久】,我们可以把u的上半部分分为两部分——一部分为所有的祖辈,一部分为同父亲的其他子树。如果说size[fa[u]][0]【注意这里暂时没有算上fa[u]】概括了所有u的祖辈【包括和u的祖辈同辈的祖宗】,那么我们需要计算的就是和u同父的子树。就比如样例中的节点2,我们需要考虑的同父亲的子树就是以4为根节点的子树。这些子树的大小我们可以直接利用已经回溯求出来了的down信息——用size[fa[u]][1] - size[u][0]。但是这里是不是多了点fa[u]的牛?没关系,因为size[fa[u]][0]正好没有fa[u]的牛,所以加起来就可以了。

总结起来,关于size:【设当前节点为u,遍历到的子节点为v

\\size[u][1] += size[v][1]; \\size[v][0] = size[u][0] + size[u][1] - size[v][1];

*注意【一个最终我都没有时间去发现的bug】:因为size[][0]是回溯前更新,size[][1]相反,所以size[][1]要先dfs一次更新出来。至此,我们dfs两次处理出size。

而后是关于far数组【相信你看到这里已经可以自己去推敲了,真的】。

首先,far[u][1]的更新和size[u][1]是同理的,回溯时利用子树已求出的距离和子树的大小更新即可。难点是far[u][0]。同样的,我们分为两部分——祖辈和同辈。祖辈的处理和子树同理,同辈稍微麻烦些。首先同辈子树到达父亲节点的不方便值,可以利用父亲的不方便值【从上往下更新的】减去所有关于当前子树贡献的部分得出;再者是从父亲节点到达当前节点的不方便值,同样的因为前面我们刚好对于父亲节点的牛的算与不算错开了所以不用考虑。

总结公式:【建议看懂了前文思路的就不要看,自己推】

\\far[u][1] += far[v][1] + size[v][1] * e[i].w; \\far[v][0] = (far[u][0] +size[u][0] * e[i].w)\\ + (size[u][1] - size[v][1]) * e[i].w\\ + (far[u][1] - far[v][1] - size[v][1]*e[i].w);

其实画个图自己就出来了。看起来是比较的冗长。其中,far[u][1]far[u][0]同样要分成两次dfs来处理。但是因为far[u][1]的处理和size[u][0]没有任何关系,所以这两部分的处理我们可以放到一个dfs中。所以最后我们需要三次dfs,而后O(n)枚举每个点的far[i][0] +far[i][1]即可。

好久没有写个题解说这么多【口水话】了……看代码啦!!

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef long long ll;
int read()
{
    int x = 0, ch = getchar();
    while(!isdigit(ch)) ch = getchar();
    while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
    return x;
}

int n, cow[maxn];
struct edge
{
    int to, w, nxt;
    edge() {}
    edge(int tt, int ww, int nn) {to = tt, w = ww, nxt = nn;}
}e[maxn << 1];

int head[maxn], k = 0;
void add(int u, int v, int w)
{
    e[k] = edge(v, w, head[u]);
    head[u] = k++;
}

bool vis[maxn]; 
ll size[maxn][2], fa[maxn], ans = 999999999999999; //洛谷数据恶毒,至少14个9。
ll far[maxn][2];//0 up 1 down
int maxpart[maxn]; 
void dfs1(int u)
{
    size[u][1] = cow[u];
    register int v;
    for(int i = head[u]; ~i; i = e[i].nxt)
    {
        v = e[i].to;
        if(v == fa[u]) continue;
        fa[v] = u;
        dfs1(v);
        size[u][1] += size[v][1];//更新1
    }
}

void dfs2(int u)
{
    register int v;
    for(int i = head[u]; ~i; i = e[i].nxt)
    {
        v = e[i].to;
        if(v == fa[u]) continue;
        size[v][0] = size[u][0] + size[u][1] - size[v][1];//更新2
        dfs2(v);
        far[u][1] += far[v][1] + size[v][1] * e[i].w;//更新3
    }
}

void dfs3(int u)
{
    register int v;
    for(int i = head[u]; ~i; i = e[i].nxt)
    {
        v = e[i].to;
        if(v == fa[u]) continue;
        far[v][0] = (far[u][0] + size[u][0] * e[i].w) + (far[u][1] - far[v][1] - size[v][1] * e[i].w) + (size[u][1] - size[v][1]) * e[i].w;//更新4
        dfs3(v);
    }
}

int main()
{
//	freopen("in.txt", "r", stdin);
    memset(head, -1, sizeof head);
    n = read();
    for(int i = 1; i <= n; i++) cow[i] = read();
    register int u, v, w;
    for(int i = 1; i < n; i++)
        u = read(), v = read(), w = read(), add(u, v, w), add(v, u, w);
        
    dfs1(1); 
    dfs2(1);
    dfs3(1);
    
    for(int i = 1; i <= n; i++)
        ans = min(ans, far[i][0] + far[i][1]);
    printf("%lld\n", ans);
    return 0;
}

迎评:)
——End——

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值