zoukankan      html  css  js  c++  java
  • 深夜敲模板_3——树的点分治(poj1741解题报告)

    具体算法可以看 2009 年的漆子超的论文

    以合法点对为例:

    进行分治,由于每次找的是重心,深度做多是log(n)。

    大体来说就是

    1:先找到该该数的重心,只需要把数遍历一遍就好了。复杂度:o(n)

    2:计算各个节点到重心的距离。复杂度:o(n)

    3:对重心距离进行排序,然后计算d[i]+d[j]<=k的合法点对。复杂度:排序o(nlog(n)),计算o(n)

    所以总的复杂度为o(n*log(n)*log(n))

    #include <iostream>
    #include <cstring>
    #define INF 1<<30
    using namespace std;
    
    struct Edge{
        int next,to;
        int w;
    }edge[MAXN<<1]; ///边的信息
    
    int son[MAXN];  ///该子树共有的节点数
    int size;       ///分治以后一颗新树所包含的所有节点数
    int list[MAXN]; ///保存遍历的这颗树的所有节点信息
    int ss[MAXN];   ///保存所有节点的子树节点数的最大值
    int d[MAXN];    ///保存节点到根的距离
    bool vis[MAXN]; ///该节点有无删除
    
    int k;          ///d[i]+d[j]<=k
    int cnt;        ///边的个数
    int num;        ///使节点值变成一个数列
    int ans;        ///保存最终答案
    int head[MAXN]; ///邻接表
    
    void add_edge(int u,int v,int w){
        edge[cnt].to = v;
        edge[cnt].w = w;
        edge[cnt].next = head[u];
        head[u] = cnt++;
    }
    
    void init(){
        cnt = ans =0;
        memset(head,-1,sizeof(head));
        memset(vis,false,sizeof(vis));
    }
    
    ///第一步,要找到重心作为根节点
    
    void dfs_size(int u,int fa){
        list[size++] = u;
        son[u] = 1;
        int tem = -1;
        for(int i = head[u];i != -1;i = edge[i].next){
            int v = edge[i].to;
            if(!vis[i] && v != fa){
                dfs_size(v,u);
                son[u] += son[v];
                tem = max(tem,son[v]);
            }
        }
        ss[u] = tem;
    }
    
    int getroot(int u){
        size = 0;
        dfs_0(u,-1);
        int m_sum = INF,rt;
        for(int i = 0;i < size;i++){
            int v = list[i];
            ss[v] = max(ss[v],size-son[v]);
            if(ss[v] < m_sum){
                m_sum = ss[v];
                rt = v;
            }
        }
        return rt;
    }
    
    ///计算所有节点到根的距离
    void dfs_dist(int u,int dis,int fa){
        d[num++] = dis;
        for(int i = head[u];i != -1;i = edge[i].next){
            int v = edge[i].to;
            if(!vis[v] && v !=fa)   dfs_dist(v,dis+edge[i].w,u);
        }
    }
    
    ///计算合法点对
    int calc(int u,int dis){
        num = 0;
        dfs_dist(u,dis,-1);
        sort(d,d+num);
        int ret = 0;
        int i = 0,j = num-1;
        while(i < j){
            while(d[i]+d[j] > k && i < j)  j--;
            ret += (j-i);
            i++;
        }
        return ret;
    }
    
    ///进行分治和计算最终答案
    void dfs(int u){
        int rt = getroot(u);
        ans += calc(rt,0);
        vis[rt] = true;
        for(int i = head[rt];i != -1;i = edge[i].next){
            int v = edge[i].to;
            if(!vis[v]){
                ans -= calc(v,edge[i].w);
                dfs(v);
            }
        }
    }
    


  • 相关阅读:
    两个数组的交集
    左叶子之和
    下载安装python
    占位
    2020 软件工程实践 助教总结
    安装使用 QEMU-KVM 虚拟化环境(Arch Linux / Manjaro / CentOS / Ubuntu )
    #69. 新年的QAQ
    1097E. Egor and an RPG game(Dilworth定理)
    #553. 【UNR #4】己酸集合
    #2099. 「CQOI2015」标识设计(插头dp)
  • 原文地址:https://www.cnblogs.com/hqwhqwhq/p/4555882.html
Copyright © 2011-2022 走看看