(模板)luoguP3806(树上点分治模板题)
点分治的写法1:
题目链接:https://www.luogu.org/problem/P3806
题意:给出一颗带边权的树,结点数n<=1e4,每条边有权值<=1e4,有m组询问(m<=100),每组询问为一个k,表示是否存在一条路经长度为k,存在输出AYE,不存在输出NAY。
思路:点分治模板题,第一次学点分治。这位聚聚的讲解特别好,安利一波:https://blog.csdn.net/a_forever_dream/article/details/81778649。
写法1:先计算所有组合情况,然后删除全部在子树中的路径,再递归求解。这种写法一般在统计答案时要排序,根据单调型或二分查找。常数比写法二要大。
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; const int inf=0x3f3f3f3f;
const int maxn=;
struct node1{
int v,w,nex;
}edge[maxn<<]; struct node2{
int x,y;
}arr[maxn]; int n,m,cnt,size;
int head[maxn],root,Min,sz[maxn],mson[maxn],vis[maxn];
int t,tt,dis[maxn],ask[],ans[]; void adde(int u,int v,int w){
edge[++cnt].v=v;
edge[cnt].w=w;
edge[cnt].nex=head[u];
head[u]=cnt;
} void getroot(int u,int fa){
sz[u]=,mson[u]=; //mson记录最大子树的大小
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].v;
if(vis[v]||v==fa) continue; //vis记录是否分治过
getroot(v,u);
sz[u]+=sz[v];
if(sz[v]>mson[u]) mson[u]=sz[v];
}
if(size-sz[u]>mson[u]) mson[u]=size-sz[u];
if(Min>mson[u]) Min=mson[u],root=u;
} void getdis(int u,int fa,int len){ //len表示u到目标点的距离
dis[++t]=len;
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].v;
if(v==fa||vis[v]) continue;
getdis(v,u,len+edge[i].w);
}
} void solve(int x,int y,int f){ //x是geidis的起点,y是x到目标点的距离,f表示合法还是不合法
t=;
getdis(x,,y); //算出树中点到目标点的距离
tt=;
sort(dis+,dis+t+);
dis[]=-;
for(int i=;i<=t;++i) //把距离相等的整合在一起,方便处理
if(dis[i]!=dis[i-]) arr[++tt].x=dis[i],arr[tt].y=;
else ++arr[tt].y;
for(int i=;i<=m;++i){
if(ask[i]%==) //单独处理到根的距离为k/2的点,它们不需二分
for(int j=;j<=tt;++j)
if(arr[j].x==ask[i]/)
ans[i]+=(arr[j].y-)*arr[j].y/*f;
for(int j=;j<=tt&&arr[j].x<ask[i]/;++j){ //仅枚举小于k/2的
int l=j+,r=tt,mid;
while(l<=r){
mid=(l+r)>>;
if(arr[j].x+arr[mid].x==ask[i]){
ans[i]+=arr[j].y*arr[mid].y*f;
break;
}
if(arr[j].x+arr[mid].x>ask[i]) r=mid-;
else l=mid+;
}
}
}
} void fenzhi(int u,int ssize){ //ssize是当前这颗子树的大小
vis[u]=;
solve(u,,); //计算这颗树以u为重心的所有组合
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].v;
if(vis[v]) continue;
solve(v,edge[i].w,-); //减去不合法组合
Min=inf,root=;
size=sz[v]<sz[u]?sz[v]:(ssize-sz[u]);
getroot(v,);
fenzhi(root,size);
}
} int main(){
scanf("%d%d",&n,&m);
for(int i=;i<n;++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
adde(u,v,w);
adde(v,u,w);
}
for(int i=;i<=m;++i)
scanf("%d",&ask[i]);
root=,Min=inf,size=n;
getroot(,);
fenzhi(root,n);
for(int i=;i<=m;++i)
if(ans[i]>) printf("AYE\n");
else printf("NAY\n");
return ;
}
点分治写法2:
题目链接:https://www.luogu.org/problem/P4149
题意:给定一颗树,求路径长为k的边数最小的路径,求最小边数。
思路:点分治裸体。写法2按子树依次递归进入,进入后先统计答案,再统计相关值,一般用桶来统计。统计答案用到了前面子树的信息。写法2的常数小于写法1。
AC代码:
#include<cstdio>
#include<algorithm>
#include<cctype>
using namespace std; const int maxn=;
const int maxk=;
const int inf=0x3f3f3f3f; inline int read(){
int x=,f=;char c=;
while(!isdigit(c)) {f|=c=='-';c=getchar();}
while(isdigit(c)) x=(x<<)+(x<<)+(c^),c=getchar();
return f?-x:x;
} struct node{
int v,w,nex;
}edge[maxn<<]; int n,ans=inf,cnt,k,head[maxn],root,size,Min,sz[maxn],mson[maxn];
int vis[maxn],mine[maxk],dis1[maxn],dis2[maxn],t,tt; void adde(int u,int v,int w){
edge[++cnt].v=v;
edge[cnt].w=w;
edge[cnt].nex=head[u];
head[u]=cnt;
} void getroot(int u,int fa){
sz[u]=,mson[u]=;
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].v;
if(vis[v]||v==fa) continue;
getroot(v,u);
sz[u]+=sz[v];
if(sz[v]>mson[u]) mson[u]=sz[v];
}
if(size-sz[u]>mson[u]) mson[u]=size-sz[u];
if(mson[u]<Min) Min=mson[u],root=u;
} void getdis(int u,int fa,int d1,int d2){
if(d1>k) return;
dis1[++t]=d1,dis2[t]=d2;
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].v;
if(vis[v]||v==fa) continue;
getdis(v,u,d1+edge[i].w,d2+);
}
} void solve(int u){
mine[]=,t=; //考虑一个端点是u的情况
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].v;
if(vis[v]) continue;
tt=t;
getdis(v,u,edge[i].w,);
for(int j=tt+;j<=t;++j) //更新答案
ans=min(ans,mine[k-dis1[j]]+dis2[j]);
for(int j=tt+;j<=t;++j) //更新桶
mine[dis1[j]]=min(mine[dis1[j]],dis2[j]);
}
for(int i=;i<=t;++i)
mine[dis1[i]]=inf;
} void fenzhi(int u,int ssize){
vis[u]=;
solve(u);
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].v;
if(vis[v]) continue;
Min=inf,root=;
size=sz[v]<sz[u]?sz[v]:(ssize-sz[u]);
getroot(v,);
fenzhi(root,size);
}
} int main(){
n=read(),k=read();
for(int i=;i<n;++i){
int u=read()+,v=read()+,w=read();
adde(u,v,w);
adde(v,u,w);
}
Min=inf,root=,size=n;
getroot(,);
for(int i=;i<=k;++i)
mine[i]=inf;
fenzhi(root,n);
printf("%d\n",ans<n?ans:-);
return ;
}
(模板)luoguP3806(树上点分治模板题)的更多相关文章
- BZOJ 1468 Tree 【模板】树上点分治
#include<cstdio> #include<algorithm> #define N 50010 #define M 500010 #define rg registe ...
- 【模板】P3806点分治1
[模板]P3806 [模板]点分治1 很好的一道模板题,很无脑经典. 讲讲淀粉质吧,很营养,实际上,点分治是树上的分治算法.根据树的特性,树上两点的路径只有一下两种情况: 路径经过根\((*)\) 路 ...
- 洛谷P2634 [国家集训队]聪聪可可 点分治模板
题意 在一棵树上任意选两个点,求它们距离模3为0的概率. 分析 树分治模板 Code #include<bits/stdc++.h> #define fi first #define se ...
- POJ1741 tree (点分治模板)
题目大意: 给一棵有 n 个顶点的树,每条边都有一个长度(小于 1001 的正整数).定义 dist(u,v)=节点 u 和 v 之间的最小距离.给定一个整数 k,对于每一对 (u,v) 顶点当且仅当 ...
- bzoj 2152 聪聪可可(点分治模板)
2152: 聪聪可可 Time Limit: 3 Sec Memory Limit: 259 MBSubmit: 3194 Solved: 1647[Submit][Status][Discuss ...
- codeforces 161D Distance in Tree 树上点分治
链接:https://codeforces.com/contest/161/problem/D 题意:给一个树,求距离恰好为$k$的点对是多少 题解:对于一个树,距离为$k$的点对要么经过根节点,要么 ...
- POJ1741 Tree(树的点分治基础题)
Give a tree with n vertices,each edge has a length(positive integer less than 1001).Define dist(u,v) ...
- 树上点分治 poj 1741
Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v ...
- 写一个迷你版Smarty模板引擎,对认识模板引擎原理非常好(附代码)
前些时间在看创智博客韩顺平的Smarty模板引擎教程,再结合自己跟李炎恢第二季开发中CMS系统写的tpl模板引擎.今天就写一个迷你版的Smarty引擎,虽然说我并没有深入分析过Smarty的源码,但是 ...
随机推荐
- Non-standard serial port baud rate setting
////combuad_recv.cpp #include <stdio.h> /*标准输入输出定义*/ #include <stdlib.h> /*标准函数库定义*/ #in ...
- whatis
whatis 查看命令的说明文档位置及说明 是从数据库中取查找 手动更新数据库 并不是每个命令都有,
- Android项目实战登录&注册
由于项目中大部分界面都有一个后退键和一个标题栏,为避免代码冗杂以及便于利用,我们可以将后推荐和标题栏单独抽取出来定义一个标题栏布局,在 res/layout 目录下新建一个 Layout resour ...
- CodeForces 755D PolandBall and Polygon ——(xjbg)
每次连线,起点和终点之间,每一个被点亮的点,这些点都能连出去两条线,因此可以增加的块数+2(1这个点除外,因为只有连出的点没有连进的点),计算起点和终点之间有几个点被点亮即可,然后1这个点特判一下.感 ...
- Palindrome Degree(CodeForces 7D)—— hash求回文
学了kmp之后又学了hash来搞字符串.这东西很巧妙,且听娓娓道来. 这题的题意是:一个字符串如果是回文的,那么k值加1,如果前一半的串也是回文,k值再加1,以此类推,算出其k值.打个比方abaaba ...
- MyBatis-Plus的一些问题
图一图二分别是搜索设计师的动态sql,已知这个字段是integer类型.为什么用=号查询的时候会显示查询超时.但是我把sql打印出来的结果直接去执行,时间在一秒是可以出来结果的. 但是用like一个i ...
- pwn学习日记Day5 基础知识积累
知识杂项 int mprotect(const void *start, size_t len, int prot); mprotect()函数把自start开始的.长度为len的内存区的保护属性修改 ...
- HashMap简单介绍
哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表. 一.什么是哈希表 在讨论哈希表之 ...
- 移动端——meta标签
meta标签主要辅助HTML结构层的.meta标签不管在互联网前端还是在移动端都起了很重要的作用. <meta http-equiv="Content-type" conte ...
- kafka可视化工具安装及简单使用
一.安装 双击kafkatool_64bit.exe安装kafka可视化工具,并且C:\Windows\System32\drivers\etc配置HOSTS,打开HOSTS并添加: HOSTS添加: ...