【点分治】的学习笔记和众多例题

【前言】

  最近一段时间变成了通过题目学习算法,似乎整个人都乱套了(反思ing)

  不过还好,现在又调整为了学算法后做题。(唉,最近一段时间有点急躁,要记住万事不能速成啊)

【正题】点分治

  一句话:点分治主要用于树上路径点权统计问题。

一、【具体流程】

1,选取一个点,将无根树变成有根树
 为了使每次的处理最优,我们通常要选取树的重心。
 何为“重心”,就是要保证与此点连接的子树的节点数最大值最小,可以防止被卡。
 重心求法:
  1。dfs一次,算出以每个点为根的子树大小。
  2。记录以每个节点为根的最大子树大小
  3。判断:如果以当前节点为根更优,就更新当前根。

void getroot(int v,int fa)
{
    son[v] = 1; f[v] = 0;//f记录以v为根的最大子树的大小 
    for(int i = head[v];i;i=e[i].next)
        if(e[i].to != fa && !vis[e[i].to]) {
            getroot(e[i].to,v);//递归更新 
            son[v] += son[e[i].to];
            f[v] = max(f[v],son[e[i].to]);//比较每个子树 
        }
    f[v] = max(f[v],sum-son[v]);//别忘了以v父节点为根的子树 
    if(f[v] < f[root]) root = v;//更新当前根 
}

2、处理连通块中通过根节点的路径。
  (注意,是通过根节点的路径,所以后面要去掉同一子树内部的路径,即去重)
3、标记根节点(相当于处理后,将根节点从子树中删除)。
4、递归处理当前点为根的每棵子树。

int solve(int v)
{
    vis[v] = 1;//标记 
    for(int i = head[v];i;i=e[i].next)
        if(!vis[e[i].to]) {
            root = 0;
            sum = son[e[i].to];
            getroot(e[i].to,v);
            solve(root);//递归处理下一个连通块 
        }
}
int main()
{
    sum = f[0] = n;//初始化 
    root = 0;
    getroot(1,0);//找重心 
    solve(root);//点分治 
}

【注释】:作者是用 son[] 来表示节点x为根的子树大小,可能他人更多地是用size[]来表示,二者同意。

二、【POJ 1741 & BZOJ 1468 & BZOJ 3365】

给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K。
【题解】:
  我们找到树的重心,然后dfs,求出每个点到root的距离deep,然后对deep排序,扫描哪些点对是符合的。
  但是,点分治要求处理的路径是经过root,所以如果一条路径是在同一个子树之内的就不符合要求,所以还要对子树dfs一下,然后去重。
  接下来处理好root后,就可以处理其他连通块了,即递归其子树。
【代码】:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define N 10010
#define inf 1e9+10
struct node{
  int to,c,next;}g[N*2];
int head[N],m;
int son[N],f[N];
bool vis[N];
int d[N],deep[N];
int n,sum,root,k,ans;

void add_edge(int from,int to,int cost)
{
    g[++m].next = head[from];
    head[from] = m;
    g[m].to = to; g[m].c = cost;
}

void getroot(int v,int fa)
{
    son[v] = 1; f[v] = 0;
    for(int i = head[v];i;i=g[i].next)
        if(g[i].to != fa && !vis[g[i].to])
        {
            getroot(g[i].to,v);
            son[v] += son[g[i].to];
            f[v] = max(f[v],son[g[i].to]);
        }
    f[v] = max(f[v],sum - son[v]);
    if(f[v] < f[root]) root = v;
}

void getdeep(int v,int fa)
{
    deep[++deep[0]] = d[v];
    for(int i = head[v];i;i=g[i].next)
        if(g[i].to != fa && !vis[g[i].to])
        {
            d[g[i].to] = d[v] + g[i].c;
            getdeep(g[i].to,v);
        }
}

int cal(int v,int cost)
{
    d[v] = cost; deep[0] = 0;
    getdeep(v,0);
    sort(deep+1<
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值