📚【模板】点分治
点分治用来解决一类同技术上路径的问题。
点分治主要的思想就是:
-
找一个点,并统计经过这个点的路径;
-
删除这个点,继续查询它的子树。
实际上每次找这个分治的点,多半选的是重心,这与正确性无关,但与时间有关。
当然还有一个重头就是统计。
模板题:\(\text{luogu P3806 【模板】点分治1}\)
- 首先,要查找重心:
-
\(size_i\):以\(i\)点为根的树的大小;
-
\(weight_i\):点\(i\)的权重(子树最大大小);
-
\(totalpoint\):现在所在的树的大小;
-
\(centroid\):找到的重心。
int size[N], weight[N], totalpoint, centroaid;
void get_centroaid(int u,int p) {
size[u] = 1;
weight[u] = 0;
for(int i = head[u];i;i = edge[i].next) {
int v = edge[i].t;
if(v == p||visited[v])
continue;
get_centroaid(v,u);
size[u] += size[v];
weight[u] = std :: max(weight[u],size[v]);
}
weight[u] = std :: max(weight[u],totalpoint-size[u]);
if(!centroaid||weight[u] < weight[centroaid])
centroaid = u;
}
- 然后要开始递归
- \(visited_i\):点\(i\)是否被删除。
//in signed main();
weight[0] = n;
totalpoint = n;
get_centroaid(1,0);
solve(centroaid);
//void solve();
void solve(int u) {
visited[u] = true;
calculate(u);
for(int i = head[u];i;i = edge[i].next) {
int v = edge[i].t;
if(visited[v])
continue;
centroaid = 0;
totalpoint = size[v];
get_centroaid(v,0);
solve(centroaid);
}
}
很明显就是找到一个点,开始统计,然后继续对子树进行统计。
- 统计
-
\(point\left[\right]\):目前树上的点;
-
\(dis_i\):点\(i\)到现在选取的根节点的距离;
-
\(belong_i\):点\(i\)属于哪棵子树。
int point[N], dis[N], belong[N], point_tot;
bool comp(const int &_x,const int &_y) {
return dis[_x] < dis[_y];
}
void get_dis(int u,int p,int dist,int ancestor) {
dis[point[++point_tot] = u] = dist;
belong[u] = ancestor;
for(int i = head[u];i;i = edge[i].next) {
int v = edge[i].t;
if(v == p||visited[v])
continue;
get_dis(v,u,dist+edge[i].w,ancestor);
}
}
void calculate(int u) {
point[point_tot = 1] = u;
dis[belong[u] = u] = 0;
for(int i = head[u];i;i = edge[i].next) {
int v = edge[i].t;
if(visited[v])
continue;
get_dis(v,u,edge[i].w,v);
}
std :: sort(point+1,point+point_tot+1,comp);
for(int i = 1;i <= m;++i) {
int l = 1, r = point_tot;
if(check_point[i])
continue;
while(l < r) {
if(dis[point[l]]+dis[point[r]] > query[i])
--r;
else if(dis[point[l]]+dis[point[r]] < query[i])
++l;
else if(belong[point[l]] == belong[point[r]]) {
if(dis[point[r]] == dis[point[r-1]])
--r;
else
++l;
} else {
check_point[i] = true;
break;
}
}
}
}
对于以\(centroid\)为根节点的树,统计完距离之后,依次翻看每一个问题,用两个指针\(l\)和\(r\)遍历数组。在距离不正确或同属一颗子树时跳指针。如果合法就打上标记。
复杂度是\(\operatorname{O}(n\log^2 n+nm\log n)\)
code here
#include <stdio.h>
#include <bits/stl_algobase.h>
#include <bits/stl_algo.h>
const int N = 10010;
const int M = 110;
struct EDGE {
int t, next;
int w;
} edge[N<<1];
int edge_tot, head[N];
void add_edge(int f,int t,int w) {
edge[++edge_tot].next = head[f];
edge[edge_tot].t = t;
edge[edge_tot].w = w;
head[f] = edge_tot;
}
int n, m, query[M];
bool check_point[M];
bool visited[N];
int size[N], weight[N], totalpoint, centroaid;
void get_centroaid(int u,int p) {
size[u] = 1;
weight[u] = 0;
for(int i = head[u];i;i = edge[i].next) {
int v = edge[i].t;
if(v == p||visited[v])
continue;
get_centroaid(v,u);
size[u] += size[v];
weight[u] = std :: max(weight[u],size[v]);
}
weight[u] = std :: max(weight[u],totalpoint-size[u]);
if(!centroaid||weight[u] < weight[centroaid])
centroaid = u;
}
int point[N], dis[N], belong[N], point_tot;
bool comp(const int &_x,const int &_y) {
return dis[_x] < dis[_y];
}
void get_dis(int u,int p,int dist,int ancestor) {
dis[point[++point_tot] = u] = dist;
belong[u] = ancestor;
for(int i = head[u];i;i = edge[i].next) {
int v = edge[i].t;
if(v == p||visited[v])
continue;
get_dis(v,u,dist+edge[i].w,ancestor);
}
}
void calculate(int u) {
point[point_tot = 1] = u;
dis[belong[u] = u] = 0;
for(int i = head[u];i;i = edge[i].next) {
int v = edge[i].t;
if(visited[v])
continue;
get_dis(v,u,edge[i].w,v);
}
std :: sort(point+1,point+point_tot+1,comp);
for(int i = 1;i <= m;++i) {
int l = 1, r = point_tot;
if(check_point[i])
continue;
while(l < r) {
if(dis[point[l]]+dis[point[r]] > query[i])
--r;
else if(dis[point[l]]+dis[point[r]] < query[i])
++l;
else if(belong[point[l]] == belong[point[r]]) {
if(dis[point[r]] == dis[point[r-1]])
--r;
else
++l;
} else {
check_point[i] = true;
break;
}
}
}
}
void solve(int u) {
visited[u] = true;
calculate(u);
for(int i = head[u];i;i = edge[i].next) {
int v = edge[i].t;
if(visited[v])
continue;
centroaid = 0;
totalpoint = size[v];
get_centroaid(v,0);
solve(centroaid);
}
}
signed main() {
scanf("%d %d",&n,&m);
for(int i = 1, f, t, w;i < n;++i) {
scanf("%d %d %d",&f,&t,&w);
add_edge(f,t,w);
add_edge(t,f,w);
}
for(int i = 1;i <= m;++i) {
scanf("%d",&query[i]);
if(!query[i])
check_point[i] = true;
}
weight[0] = n;
totalpoint = n;
get_centroaid(1,0);
solve(centroaid);
for(int i = 1;i <= m;++i)
printf("%s\n",check_point[i] ? "AYE" : "NAY");
}