点分治

点分治:点分治,是处理树上路径的一个极好的工具。一般如果需要大规模处理树上路径,点分治是一个不错的选择。

推荐大佬的一个视频: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% 的数据,保证 n100。

对于 60% 的数据,保证 n1000,m50 。

对于 100% 的数据,保证 1n10^4,1m100,1k10^7,1u,vn,1w10^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 }

 

posted @ 2020-10-03 22:13  swsyya  阅读(182)  评论(0编辑  收藏  举报

回到顶部