算法学习:点分治

【定义】

【树的重心】所有子树的大小都不超过整个树大小的一半的点

【重心的求取】依次查找每个点,当此点的最大子树最小时肯定是重心


【解决问题】

图上处理距离

类似这样:

一张图内n个点,求距离等于k的点对个数

一张图内n个点,距离等于质数的点的对数

一张图内n个点,是否存在距离等于k的点

.....................................


【解决思路】

 直接莽,n^3的复杂度,肯定炸

所以我们需要想一种方法,能够显著减少其复杂度,于是就用到了分治

选取一个节点作为将这个DAG的根,使其变成一颗树,

想象一手拿着根,提溜着抖搂一下然后就成了一颗树,下面有若干颗子树

于是距离符合要求的情况有两种,

第一种:两个点都在同一颗子树中

第二种:两个点在不同子树中,但是很明显他们都经过了根节点

所以我们计算出各个点到根节点的距离,然后排序比较一番就能够在nlogn的时间内求得答案,就能够解决第二种情况的问题

而第一种情况,我们再向下继续进行找根变成树的操作,他们两个也会在不同的子树中

这样子,第一种情况就会变成第二种,我们也就只需要考虑如何处理第二种和如何把第一种变成第二种

第一种变成第二种的关键在于这个根的选取,如果选的根不对很有可能选出来一个链,然后就O(n)处理,果断GG

怎么分治才能使层数尽量小,使两边尽量均衡,达到二分的效果的情况下层数更少,能够缩减至logn,所以我们需要取重心做,

这个根是重心,所以下面的子树的层数会是最少的

#include<map>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 10010;
const int MAXM = 20010;
const int MAXK = 10000010;
struct node
{
    int to;
    int val;
    int nt;
}edge[MAXM];
int  st[MAXN],  ask[110],top;
int  siz[MAXN], f[MAXN], root;
int  dis[MAXN], a[MAXN], S,cnt,n,m;
int  ans[MAXK];
bool vis[MAXN];
void add(int x,int y,int z)
{
    top++; edge[top] = { y,z,st[x] }; st[x] = top;
}
void get_root(int x, int fa)
{
    siz[x] = 1, f[x] = 0;
    for (int i = st[x]; i; i = edge[i].nt)
    {
        int to = edge[i].to;
        if (to == fa || vis[to]) continue;
        get_root(to, x);
        f[x] = max(f[x], siz[to]);
        siz[x] += siz[to];
    }
    f[x] = max(f[x], S - siz[x]);
    if (f[x] < f[root])
    {
        root = x;
    }
}
void get_dis(int x, int fa,int d)
{
    dis[++cnt] = a[x];
    for (int i = st[x]; i; i = edge[i].nt)
    {
        int to = edge[i].to;
        if (vis[to] || to == fa) continue;
        a[to] = d + edge[i].val;
        get_dis(to, x,a[to]);
    }
}
void solve(int x,int len,int w)
//
{
    cnt = 0;
    a[x] = len;
    get_dis(x, 0, len);
    for(int i=1;i<=cnt;i++)
        for (int j = 1; j <= cnt; j++)
        {
            if (i != j && dis[i]+dis[j]<MAXK)
                ans[dis[i] + dis[j]] += w;
        }
}
void divide(int x)
{
    solve(x, 0, 1);
    vis[x] = 1;
    for (int i = st[x]; i; i = edge[i].nt)
    {
        int to = edge[i].to;
        if (vis[to]) continue;
        solve(to, edge[i].val, -1);
        S = siz[x], root = 0, f[0] = n;
        get_root(to, x);
        divide(root);
    }
}
int main()
{
    scanf("%d%d", &n, &m);
        for (int i = 1; i <= n-1; i++)
        {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            add(x, y, z);
            add(y, x, z);
        }
        siz[0] = 0,f[0] = n, root = 0,S=n;
        get_root(1, 0);
        divide(root);
        for (int i = 1; i <= m; i++)
        {
            int k;
            scanf("%d", &k);
            if (ans[k])
                printf("AYE\n");
            else
                printf("NAY\n");
        }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/rentu/p/11284495.html
今日推荐