点分治

点分治

一般用于解决树上关于点对的问题(路径也可视作点对)。

基础写法

对于每个点对,我们在它的LCA处统计贡献。
于是为了减小复杂度(具体不会证),我们每次找到当前联通块的重心,然后统计这个以这个点为LCA的点对的贡献。
大致的结构就是这样:(随便找了一题代码放上来的)

其中后面加了

//~~~~~~

的基本上都是一些基础的结构,在大多数题目中都是一样的。

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define LL long long
#define AC 100100
#define ac 201000
#define maxn 10000100

int n, p, rot, ss, top1, top; LL ans;
int Head[AC], date[ac], Next[ac], tot;
int v[AC], bu[maxn], f[AC], Size[AC];
bool z[AC];

struct node{
    int sum, max;
}s[ac], b[ac];

inline bool cmp(node a, node b) {return a.max < b.max;}

inline int read()
{
    int x = 0;char c = getchar();
    while(c > '9' || c < '0') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x;
}

inline void upmin(int &a, int b) {if(b < a) a = b;}
inline void upmax(int &a, int b) {if(b > a) a = b;}
inline void up(int &a, int b) {if(b >= p || b <= -p) b %= p; a += b; if(a < 0) a += p; if(a >= p) a -= p;}
inline void imul(int &a, int b) {a = 1LL * a * b % p;}
inline int ad(int a, int b) {if(b >= p || b <= -p) b %= p; a += b; if(a < 0) a += p; if(a >= p) a -= p; return a;}
inline int mul(int a, int b) {return 1LL * a * b % p;}

inline void add(int f, int w)
{
    date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot;
    date[++ tot] = f, Next[tot] = Head[w], Head[w] = tot;
}

void getrot(int x, int fa)//f[x]表示x子树中最重的那个的重量
{
    f[x] = 0, Size[x] = 1;
    for(R i = Head[x]; i; i = Next[i])
    {
        int now = date[i];
        if(z[now] || now == fa) continue;
        getrot(now, x), Size[x] += Size[now];
        upmax(f[x], Size[now]);
    }
    upmax(f[x], ss - Size[x]);//因为无根,所以还要获得上面那一坨的重量
    if(f[x] < f[rot]) rot = x;
}

void dfs(int x, int fa, int w, int sum)//w为现在路径中的最大值 ,sum为路径权值在mod p意义下的权值和
{
    /*do something*/
}

void cal(int x)
{
    /*do something*/
}

void solve(int x)
{
    cal(x);//~~~~~~
    for(R i = Head[x]; i; i = Next[i])
    {
        int now = date[i];
        if(z[now]) continue;
        rot = 0, f[0] = ss = Size[now];//~~~~~~
        getrot(now, 0), solve(rot);//~~~~~~
    }
}

void pre()
{
    n = read(), p = read();
    for(R i = 2; i <= n; i ++) add(read(), read());
    for(R i = 1; i <= n; i ++) v[i] = read();
}

void work()
{
    ans = n;//每个点单独算一次
    rot = 0, f[0] = ss = n;//~~~~~~
    getrot(1, 0), solve(rot);//~~~~~~
    printf("%lld\n", ans);
}

int main()
{
    //freopen("in.in", "r", stdin);
    pre();
    work();
    //fclose(stdin);
    return 0;
}

点分树

点分树是一个严格log的树,可以用点分治来构造。
假设我们当前所在节点是\(x\), 我们遍历它周围的各个子树,将这些子树内的重心作为\(x\)的儿子。
最后就构建出了点分树。

动态点分治

因为点分树是一个严格log的树,所以如果我们修改了点\(x\),那我们就直接暴力重新统计\(x\)以及它的祖先节点的贡献,然后一般会在每个点上开个数据结构来维护一下子树内信息,复杂度是\(O(nlog^2n)\)的。

posted @ 2019-04-03 17:23  ww3113306  阅读(198)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。