深夜敲模板_3——树的点分治(poj1741解题报告)

来源:互联网 发布:lvm安装linux 编辑:程序博客网 时间:2024/05/16 17:59

具体算法可以看 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<<30using 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]<=kint 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);        }    }}


0 0
原创粉丝点击