點分治
一般用於解決樹上關於點對的問題(路徑也可視作點對)。
基礎寫法
對於每個點對,我們在它的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)\)的。