點分治

點分治

一般用於解決樹上關於點對的問題(路徑也可視作點對)。

基礎寫法

對於每個點對,我們在它的LCA處統計貢獻。
於是爲了減小複雜度(具體不會證),我們每次找到當前聯通塊的重心,然後統計這個以這個點爲LCT的點對的貢獻。
大致的結構就是這樣:(隨便找了一題代碼放上來的)

其中後面加了

//~~~~~~

的基本上都是一些基礎的結構,在大多數題目中都是一樣的。

#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)\)的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章