点分治
一般用于解决树上关于点对的问题(路径也可视作点对)。
基础写法
对于每个点对,我们在它的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
", ans);
}
int main()
{
//freopen("in.in", "r", stdin);
pre();
work();
//fclose(stdin);
return 0;
}
点分树
点分树是一个严格log的树,可以用点分治来构造。
假设我们当前所在节点是(x), 我们遍历它周围的各个子树,将这些子树内的重心作为(x)的儿子。
最后就构建出了点分树。
动态点分治
因为点分树是一个严格log的树,所以如果我们修改了点(x),那我们就直接暴力重新统计(x)以及它的祖先节点的贡献,然后一般会在每个点上开个数据结构来维护一下子树内信息,复杂度是(O(nlog^2n))的。