luogu P3806 模板点分治1
Posted qaqq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu P3806 模板点分治1相关的知识,希望对你有一定的参考价值。
给定一棵有n个点的树
询问树上距离为k的点对是否存在。
(多次询问&&可离线)
我们先随意指定一个虚拟根root,将这棵树转化成无根树
树上的路径可以分为两类,
1.经过根节点u的路径
2.完全在u子树里(不经过u)的
对于1,用dis表示当前结点到根节点root的路径长度, 则root的子树中两个节点u到v的路径长即为dis[u]+dis[v]
对于2,u到v的路径完全在root的某个子树内, 那么就找到这棵子树的根,对它再求一次第一种路径
就是不断的寻找重心,把原来的树分成很多小的树,并对每个子树分别求解
maxx[u]表示u的儿子的子树中,最大的大小
则树的重心就是maxx值最小的那个节点
1 #include<cstdio> 2 #include<iostream> 3 #define maxn 100010 4 using namespace std; 5 int n,m,text[maxn],t[maxn],root; 6 int sum,size[maxn],maxx[maxn]; 7 int head[maxn*3],nxt[maxn*3],to[maxn*3],val[maxn*3],cnt; 8 int vis[maxn],vis1[maxn],vis2[maxn],dis[maxn],judge[10000010]; 9 void add(int a,int b,int v) 10 { 11 cnt++; 12 nxt[cnt]=head[a]; 13 head[a]=cnt; 14 to[cnt]=b; 15 val[cnt]=v; 16 } 17 void getroot(int u,int fa)//找到以u为跟的子树的重心 18 { 19 size[u]=1; 20 for(int i=head[u];i;i=nxt[i]) 21 { 22 if(to[i]==fa||vis[to[i]]) continue; 23 getroot(to[i],u); 24 size[u]+=size[to[i]];//u的子树的大小包括所有子树的大小的和加一 25 maxx[u]=max(maxx[u],size[to[i]]); 26 } 27 maxx[u]=max(maxx[u],sum-maxx[u]);//保存u把整棵树分成两部分后大的部分的大小 28 if(maxx[u]<maxx[root]) root=u;//重心几乎把树分成平均两部分 29 return; 30 } 31 void getdis(int u,int f) 32 { 33 vis2[++vis2[0]]=dis[u]; //保存root到根节点中每一个点的距离 34 for(int i=head[u];i;i=nxt[i]) 35 { 36 if(vis[to[i]]||to[i]==f) continue; 37 dis[to[i]]=dis[u]+val[i]; //dis[u]保存此时的root到u的距离 38 getdis(to[i],u); 39 } 40 return; 41 } 42 void qiu(int u) 43 { 44 vis1[0]=0; 45 for(int i=head[u];i;i=nxt[i]) 46 { 47 if(vis[to[i]]) continue; 48 vis2[0]=0; 49 dis[to[i]]=val[i]; 50 getdis(to[i],u); 51 for(int j=1;j<=vis2[0];j++) 52 for(int o=1;o<=m;o++) 53 if(text[o]>=vis2[j]&&t[o]==0) t[o]=judge[text[o]-vis2[j]]; 54 //若u的子树中存在两条以u为节点的不同路径经长度和为 text[o],那么就存在长度为text[u]的路径 55 for(int j=1;j<=vis2[0];j++) 56 { 57 vis1[++vis1[0]]=vis2[j]; 58 judge[vis2[j]]=1; 59 //保存存在的从root到u的子树的点的路径长度 60 } 61 } 62 for(int i=1;i<=vis1[0];i++) judge[vis1[i]]=0;//下一次不经过u及其子树,删去因此存在的路径长度 63 return; 64 } 65 void solve(int u) //进行点分治 66 { 67 vis[u]=1; 68 judge[0]=1;//解决一条路径端点分别为root与某个子节点时,这条路径可以直接充当答案,相当于与一条长为0的路径进行了第一种情况,所以长度为0的路径永远存在 69 qiu(u); 70 for(int i=head[u];i;i=nxt[i]) 71 { 72 if(vis[to[i]]) continue; 73 sum=size[to[i]]; 74 root=0; 75 maxx[0]=2e9;//最开始设一个虚拟的点为重心 76 getroot(to[i],0); 77 solve(root); 78 } 79 return; 80 } 81 int main() 82 { 83 scanf("%d%d",&n,&m); 84 for(int i=1;i<n;i++) 85 { 86 int a,b,c; 87 scanf("%d%d%d",&a,&b,&c); 88 add(a,b,c); 89 add(b,a,c); 90 } 91 for(int i=1;i<=m;i++) 92 scanf("%d",&text[i]); //离线计算 ,保存每个k 93 maxx[root]=n; 94 sum=n; 95 getroot(1,0); //找到整颗树的重心 96 solve(root); //由整颗树的重心开始点分治 97 for(int i=1;i<=m;i++) 98 { 99 if(t[i]) printf("AYE "); 100 else printf("NAY "); 101 } 102 return 0; 103 }
以上是关于luogu P3806 模板点分治1的主要内容,如果未能解决你的问题,请参考以下文章