树dp...吧 ZOJ 3949

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5568

 

Edge to the Root

Time Limit: 1 Second      Memory Limit: 131072 KB

Given a tree with n vertices, we want to add an edge between vertex 1 and vertex x, so that the sum of d(1, v) for all vertices v in the tree is minimized, where d(uv) is the minimum number of edges needed to pass from vertex u to vertex v. Do you know which vertex x we should choose?

Recall that a tree is an undirected connected graph with n vertices and n - 1 edges.

Input

There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:

The first line contains an integer n (1 ≤ n ≤ 2 × 105), indicating the number of vertices in the tree.

Each of the following n - 1 lines contains two integers u and v (1 ≤ uv ≤ n), indicating that there is an edge between vertex u and v in the tree.

It is guaranteed that the given graph is a tree, and the sum of n over all test cases does not exceed 5 × 105. As the stack space of the online judge system is not very large, the maximum depth of the input tree is limited to about 3 × 104.

We kindly remind you that this problem contains large I/O file, so it's recommended to use a faster I/O method. For example, you can use scanf/printf instead of cin/cout in C++.

Output

For each test case, output a single integer indicating the minimum sum of d(1, v) for all vertices v in the tree (NOT the vertex x you choose).

Sample Input

2
6
1 2
2 3
3 4
3 5
3 6
3
1 2
2 3

Sample Output

8
2

Hint

For the first test case, if we choose x = 3, we will have

d(1, 1) + d(1, 2) + d(1, 3) + d(1, 4) + d(1, 5) + d(1, 6) = 0 + 1 + 1 + 2 + 2 + 2 = 8

It's easy to prove that this is the smallest sum we can achieve.


Author: WENG, Caizhi
Source: The 17th Zhejiang University Programming Contest Sponsored by TuSimple

 

题目大意:给你一棵树,这棵树的每条边的length都是1。然后这棵树是以1为root,定义他的weight就是所有的节点的deep和。

现在你有一个操作,对于任意的u,可以从(1,u)连接一条边,问选择哪个节点u连接可以使得树的weight最小。

 

思路:其实很早就有思路了...但是最近心情太烦了,写的时候都很烦,就没有做。

刚开始sb的用线段树啊,树状数组啊来维护,发现好烦。然后第二天删光了所有的代码重新来,但还是摆脱不了树状数组...(感觉就算写对了应该也是TLE了)

今天突然发现可以维护一下前缀就好了(真的是......)

 

首先我们可以发现,对于(1,u)连接了边以后,那么深度为deep[u]/2 + 1的这些点的deep都会发生改变。

我们从1开始进行dfs,然后我们对于经过的所有的节点,都把他定义为deep = 1.然后定义前面一个子树的区间为[l, r],当前的区间为[L, R],然后利用这个进行修改即可

具体的就是容斥一下就好了,还不懂看着代码里面的注释,然后自己画一画

 

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int maxn = 2e5 + 5;
int n;
vector<int> G[maxn];
int deep[maxn], sz[maxn];
LL cnt[maxn];

void dfs_sz(int u, int fa, int d){
    deep[u] = d; sz[u] = 1; cnt[u] = d;
    for (int i = 0; i < G[u].size(); i++){
        int v = G[u][i];
        if (v == fa) continue;
        dfs_sz(v, u, d + 1);
        sz[u] += sz[v];
        cnt[u] += cnt[v];
    }
}

LL ans;
LL rest, res, addval;
///rest表示每次需要加回来的东西是多少,addval表示rest的和
///res表示把路上经过的点deep都变成1所剩下的val
LL pre[maxn];//表示深度为l的有多少节点是被修改了的
void dfs_solve(int u, int fa, int l, int r){
    pre[deep[u]] = sz[u];
    int L = deep[u] / 2 + 1, R = deep[u];
    if (R > r) res -= sz[u] * (deep[u] - 1);///减去子树的
    if (L > l) rest -= pre[l];///减去之前子树的size
    addval += rest;
    ans = min(ans, addval + res);
    for (int i = 0; i < G[u].size(); i++){
        int v = G[u][i];
        if (v == fa) continue;
        pre[deep[u]] -= sz[v];
        rest += pre[deep[u]];
        res += (deep[u] - 1) * sz[v];
        dfs_solve(v, u, L, R);
        res -= (deep[u] - 1) * sz[v];
        rest -= pre[deep[u]];
        pre[deep[u]] += sz[v];
    }
    if (R > r) res += sz[u] * (deep[u] - 1);
    addval -= rest;
    if (L > l) rest += pre[l];
}

int main(){
    int t; cin >> t;
    while (t--){
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) G[i].clear();
        for (int i = 1; i < n; i++){
            int u, v; scanf("%d%d", &u, &v);
            G[u].pb(v), G[v].pb(u);
        }
        dfs_sz(1, 0, 0);
        res = ans = cnt[1];
        addval = 0;
        for (int i = 0; i < G[1].size(); i++){
            int v = G[1][i];
            dfs_solve(v, 1, 1, 0);
        }
        printf("%lld\n", ans);
    }
    return 0;
}
/*
456
7
1 2
2 3
3 4
3 5
5 7
4 6

ans = 12
*/
View Code

 

posted @ 2017-04-15 15:51  知る奇迹に  阅读(233)  评论(0编辑  收藏  举报