• AcWing


    https://www.acwing.com/problem/content/description/254/

    这个才是真的点分治,上次那个直接树dp就结束了,因为和子树的size没有什么关系,甚至那个重心是自己骗自己的。

    这个点分就厉害了,每次都要找新的重心。

    因为每次分治合并答案的代价是和当前子树的size有关的,所以才要重新找子树的重心分治。

    需要用容斥减去答案的原因是因为他的计算方法。

    抄别人代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN = 40000 + 5;
    
    int n, m, k, tot, all_node, ans, root;
    // root 为重心
    int head[MAXN], max_part[MAXN], siz[MAXN], len[MAXN], d[MAXN];
    // d[i] 表示i到根节点的距离
    // len[i] 表示找到的从某个根节点出发的第i条路径长度,len[0]表示路径的数量
    // max_part[i] 表示i的最大的子树大小
    bool vis[MAXN];
    // vis用于求重心时避免重复访问
    
    struct Edge {
        int next, to, dis;
    } edge[MAXN << 1];
    // edge 用于存边,记得给反向边留位置
    
    inline void add(int from, int to, int dis) {
        edge[++tot].to = to;
        edge[tot].dis = dis;
        edge[tot].next = head[from];
        head[from] = tot;
    }
    
    void get_root(int u, int fa) { // 找到树的重心
        max_part[u] = 0, siz[u] = 1;
        for(int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if(v == fa || vis[v])
                //既不要往回走,也不能越过上一次分治已经删除(vis)的点
                continue;
            get_root(v, u);
            siz[u] += siz[v];
            max_part[u] = max(max_part[u], siz[v]);
        }
        max_part[u] = max(max_part[u], all_node - siz[u]);
        if(max_part[u] < max_part[root])
            root = u;
    }
    
    void get_dis(int u, int fa) { // 求出每个点到根节点的距离
        len[++len[0]] = d[u];
        for(int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if(v == fa || vis[v])
                //既不要往回走,也不能越过上一次分治已经删除(vis)的点
                continue;
            d[v] = d[u] + edge[i].dis;
            get_dis(v, u);
        }
    }
    
    int cal(int u, int now) { // 计算以u为根的所有情况的答案,now是已经累计的路径长度
        d[u] = now, len[0] = 0;
        get_dis(u, 0);
        sort(len + 1, len + len[0] + 1);
        int all = 0;
        for(int l = 1, r = len[0]; l < r;) {
            //sort之后,和<=k的必定是中间的连续一段
            if(len[l] + len[r] <= k) {
                all += r - l;
                ++l;
            } else
                r--;
        }
        return all;
    }
    
    void solve(int u) { // 求解以u为重心的情况
        vis[u] = true;
        ans += cal(u, 0);
        for(int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if(vis[v])
                continue;
            ans -= cal(v, edge[i].dis); // 减去不合法的
            // 为什么?这里传进去的是啥?
            all_node = siz[v];
            root = 0;
            get_root(v, u);
            solve(root);
        }
    }
    
    int main() {
        while(1) {
            scanf("%d%d", &n, &k);
            if(!n && !k)
                break;
    
            tot = 0, ans = 0;
            memset(head, -1, sizeof(head));
            memset(max_part, 0, sizeof(max_part));
            memset(siz, 0, sizeof(siz));
            memset(vis, false, sizeof(vis));
    
            for(int i = 1; i < n; ++i) {
                int u, v, w;
                scanf("%d%d%d", &u, &v, &w);
                //把题目的坐标偏移到从1开始
                add(u + 1, v + 1, w);
                add(v + 1, u + 1, w);
            }
    
            all_node = n, max_part[0] = n, root = 0;
            get_root(1, -1);
            //开始分治
            solve(root);
            printf("%d
    ", ans);
        }
        return 0;
    }
    

    作者:MILLOPE
    链接:https://www.acwing.com/solution/AcWing/content/2826/
    来源:AcWing
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 相关阅读:
    bzoj1093[ZJOI2007]最大半连通子图(tarjan+拓扑排序+dp)
    tarjan强连通分量模板(pascal)
    二分图最小顶点覆盖数=最大匹配数的证明
    poj3041 Asteroids(二分图最小顶点覆盖、二分图匹配)
    bzoj4196[Noi2015]软件包管理器
    AEAI Portal 权限体系说明
    未来70%的人类将会失业
    工作中高效学习的方法
    如何正确的做事
    你真的会沟通吗? --下部
  • 原文地址:https://www.cnblogs.com/Inko/p/11642733.html
Copyright © 2020-2023  润新知