zoukankan      html  css  js  c++  java
  • hihocoder [Offer收割]编程练习赛52 D 部门聚会

    看了题目的讨论才会做的

    首先一点,算每条边(u, v)对于n*(n+1)/2种[l, r]组合的贡献
    正着算不如反着算
    哪些[l, r]的组合没有包含这条边(u, v)呢
    这个很好算
    只需要统计u这半边的点中有哪些连续数字,连续的数字就是一个[l, r]组合
    就可以算出u这半边有哪些潜在的[l, r]组合
    当然u这半边算好了,v这半边正好是u的数字反过来
    这个过程可以使用set来统计,很好写

    现在我们解决了对于一个边怎么算贡献
    现在需要使用点分治

    使用点分治求重心进行遍历保证了每个点至多被放入set log(n)次
    所以复杂度大约nlog(n)

    #include <algorithm>
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <map>
    #include <queue>
    #include <set>
    #include <stack>
    #include <vector>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    #define MS(x, y) memset(x, y, sizeof(x))
    #define MP(x, y) make_pair(x, y)
    const int INF = 0x3f3f3f3f;
    
    int n;
    struct GraphUnit {
        int to, next;
    } Tree[N << 1];
    int head[N], tot;
    bool vis[N];
    void addEdge(int from, int to) {
        Tree[tot].to = to;
        Tree[tot].next = head[from];
        head[from] = tot++;
    }
    set<int> vertexSet;
    int min_MaxSon_Centrol_Num;
    int min_MaxSon_Centrol_Pos;
    ll ans = 0;
    int getCentrol(int vertex, int preVertex) {
        int fatherSum = 1;
        int maxSon = -1;
        vertexSet.insert(vertex);
        for (int i = head[vertex]; ~i; i = Tree[i].next) {
            int to = Tree[i].to;
            if (to == preVertex || vis[to])
                continue;
            int sonSum = getCentrol(to, vertex);
            maxSon = max(maxSon, sonSum);
            fatherSum += sonSum;
        }
        maxSon = max(maxSon, n - fatherSum);
        if (maxSon < min_MaxSon_Centrol_Num) {
            min_MaxSon_Centrol_Num = maxSon;
            min_MaxSon_Centrol_Pos = vertex;
        }
        return fatherSum;
    }
    ll Cal(int x) {
        return 1ll * x * (x + 1) / 2;
    }
    void solveTheSet() {
        int pre = 0;
        int cnt = 0;
        ll preans = ans;
        for (auto it = vertexSet.begin(); it != vertexSet.end(); ++it) {
            int number = *it;
            if (number == pre + 1)
                cnt++;
            else {
                ans += Cal(cnt);
                ans += Cal(number - pre - 1);
                cnt = 1;
            }
            pre = number;
        }
        ans += Cal(cnt);
        ans += Cal(n - pre);
        //   printf("hh :%lld
    ", ans - preans);
    }
    void divideAndConquer(int vertex, int preVertex) {
        min_MaxSon_Centrol_Num = INF;
        vertexSet.clear();
        getCentrol(vertex, vertex);
        int root = min_MaxSon_Centrol_Pos;
        // printf("%d
    ", root);
        vis[root] = true;
        if (vertex != preVertex) {  // only the first we don't need to solve, because it don't have the pre edge
            solveTheSet();
        }
        for (int i = head[root]; ~i; i = Tree[i].next) {
            int to = Tree[i].to;
            if (vis[to])
                continue;
            divideAndConquer(to, root);
        }
    }
    int main() {
        while (~scanf("%d", &n)) {
            memset(vis, 0, sizeof(vis));
            memset(head, -1, sizeof(head));
            tot = 0;
            ans = 0;
            for (int i = 1; i < n; ++i) {
                int from, to;
                scanf("%d %d", &from, &to);
                addEdge(from, to);
                addEdge(to, from);
            }
    
            divideAndConquer(1, 1);
    
            printf("%lld
    ", Cal(n) * (n - 1) - ans);
        }
        return 0;
    }
    
  • 相关阅读:
    随机点名系统
    JQuery
    百度搜索下拉提示
    正则表达式
    严格模式
    CSS引入方式有哪些,区别是什么
    Js中的函数
    float浮动造成高度塌陷的解决办法
    PC端页面开发基础-问题总结(一)
    PC端页面开发基础-IE6常见CSS解析Bug及Hack
  • 原文地址:https://www.cnblogs.com/Basasuya/p/8668317.html
Copyright © 2011-2022 走看看