点分治:点分治,是处理树上路径的一个极好的工具。一般如果需要大规模处理树上路径,点分治是一个不错的选择。
推荐大佬的一个视频:here
可以看这个博客加以理解点分治:here,不过我的代码并不是用的这篇博客的
模板题:P3806 【模板】点分治1
题目描述
给定一棵有 n 个点的树,询问树上距离为 k 的点对是否存在。
输入格式
第一行两个数 n,m.
第 2 到第 n 行,每行三个整数 u, v, w,代表树上存在一条连接 u 和 v 边权为 w 的路径。
接下来 m 行,每行一个整数 k,代表一次询问。
输出格式
对于每次询问输出一行一个字符串代表答案,存在输出 AYE
,否则输出 NAY
。
输入输出样例
输入 #1
2 1 1 2 2 2
输出 #1
AYE
说明/提示
数据规模与约定
对于 30% 的数据,保证 n≤100。
对于 60% 的数据,保证 n≤1000,m≤50 。
对于 100% 的数据,保证 1≤n≤10^4,1≤m≤100,1≤k≤10^7,1≤u,v≤n,1≤w≤10^4。`
AC_Code:模板
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 1e5+10; 5 const int maxm = 1e7+1000; 6 const int inf = 0x3f3f3f3f; 7 8 struct edge{ 9 int to,w,nxt; 10 }e[maxn<<1]; 11 12 int _size,head[maxn],que[maxn],maxp[maxn],dis[maxn]; 13 int sz[maxn],ans[maxn],tot,rt; 14 bool vis[maxm],judge[maxm]; 15 int tmp[maxm]; 16 int n,m; 17 18 void addedge(int u,int v,int w){ 19 e[_size].to=v; e[_size].w=w; e[_size].nxt=head[u]; head[u]=_size++; 20 } 21 22 void dfs_zx(int u,int fa){ 23 sz[u]=1; 24 maxp[u]=0; 25 for(register int i=head[u];~i;i=e[i].nxt){ 26 int to = e[i].to; 27 if( to==fa || vis[to] ) continue; 28 dfs_zx(to,u); 29 sz[u] += sz[to]; 30 maxp[u] = max(maxp[u],sz[to]); 31 } 32 maxp[u] = max(maxp[u], tot-sz[u]); 33 if( maxp[u]<maxp[rt] ){ 34 rt = u; 35 } 36 return ; 37 } 38 39 void get_dis(int root,int fa){ 40 tmp[ ++tmp[0] ] = dis[root]; 41 for(int i=head[root];~i;i=e[i].nxt){ 42 int to=e[i].to, _dis=e[i].w; 43 if( vis[to] || to==fa ) continue; 44 dis[to] = dis[root] + _dis; 45 get_dis(to, root); 46 } 47 } 48 49 void calc(int rt){ 50 queue<int> q; 51 for(int i=head[rt];~i;i=e[i].nxt){ 52 int to=e[i].to, _dis=e[i].w; 53 if( vis[to] ) continue; //保证只往下搜 54 tmp[0] = 0; //tmp记录当前子树算出的距离,tmp[0]相当于设了一个cnt计数器 55 dis[to] = _dis; //dis[to]为rt与to之间的距离 56 get_dis(to,rt); 57 for(int j=tmp[0]; j; j--){ //枚举所有长度 58 for(int k=1; k<=m; k++){ //枚举所有询问 59 if( que[k]>=tmp[j] ){ //如果询问大于单条路径长度,那就有可能存在 60 ans[k] |= judge[que[k]-tmp[j]];//如果能用两条路径拼出来,那就存在 61 } 62 } 63 } 64 65 for(int j=tmp[0];j;j--){ //把存在的单条路径长度标上true,供下个子树用 66 q.push(tmp[j]); 67 judge[tmp[j]]=true; 68 } 69 } 70 71 while(!q.empty()) //清空judge数组,不要用memset,会超时 72 { 73 judge[q.front()]=false; 74 q.pop(); 75 } 76 } 77 78 void solve(int root){ 79 vis[root]=true; //标记访问过 80 judge[0]=true; //到当前根长度是0的肯定存在,标记true 81 calc(root); //计算经过根结点的路径 82 for(int i=head[root];~i;i=e[i].nxt){ 83 int to=e[i].to; 84 if( vis[to] ) continue; 85 maxp[rt=0] = tot = sz[to]; 86 dfs_zx(to,0); 87 dfs_zx(rt,0); 88 solve(rt); 89 } 90 } 91 92 void init(){ 93 memset(head,-1,sizeof(head)); 94 _size=0; 95 } 96 97 int main() 98 { 99 init(); 100 101 cin>>n>>m; 102 for(int i=1;i<n;i++){ 103 int u,v,w; cin>>u>>v>>w; 104 addedge(u,v,w); addedge(v,u,w); 105 } 106 for(int i=1;i<=m;i++) cin>>que[i]; 107 maxp[rt=0] = n; //相当于设了一个maxx 108 tot = n; //记录当前树的大小 109 dfs_zx(1,0); //找重心 //此时siz数组存放的是以1为根时的各树大小,需要以找出的重心为根重算 110 dfs_zx(rt,0); 111 solve(rt); 112 for(int i=1;i<=m;i++){ 113 if( ans[i] ) cout<<"AYE"<<endl; 114 else cout<<"NAY"<<endl; 115 } 116 return 0; 117 }