(模板)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的源码,但是 ...
随机推荐
- 虚拟机里的nginx启动不了的问题
问题: 之前使用虚拟机nginx一直都是正常的, 就在昨天重新配置了一下nginx(其实只注释了反向代理,别的都没动), 重启nginx, 却报错pid为空. 无法启动nginx. 而且重启之前 ...
- C# params object[] args 可以传多个参数,可以不限制类型
http://blog.csdn.net/wslyy99/article/details/1836486 C#参数数组(params)的使用方法 http://www.vcsos.com/articl ...
- learing cbor protocol
https://tools.ietf.org/html/rfc7049 https://github.com/panzidongfamily/tinycbor
- 路由器配置——OSPF协议(1)
一.实验目的:用OSPF协议使全网互通 二.拓扑图 三.具体步骤配置 (1)R1路由器配置 Router>enableRouter#configure terminalEnter configu ...
- Java并发之ThreadPoolExecutor
ThreadPoolExecutor源码分析 简介 java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必 ...
- JavaWeb_(Mybatis框架)使用Mybatis对表进行增、删、改、查操作_二
系列博文: JavaWeb_(Mybatis框架)JDBC操作数据库和Mybatis框架操作数据库区别_一 传送门 JavaWeb_(Mybatis框架)使用Mybatis对表进行增.删.改.查操作_ ...
- ACM之路(17)—— 博弈论
博弈论这方面网上资料庞大,我觉得我不可能写的比他们好,就转载一下我觉得写的不错的博客好了. 首先是三大博弈:巴什博奕,威佐夫博奕,尼姆博奕.博客:三大基本博弈. 然后是强大的sg函数和sg定理:SG. ...
- Raspberry Pi 4B基本设置
目录 一.SSH登录Raspberry Pi 二.开启VNC服务 三.将SD卡分区扩展 四.修改软件源 一.SSH登录Raspberry Pi 完成系统烧录后,就需要登录Raspberry Pi,此时 ...
- Guava中Lists.partition(List, size) 方法懒划分/懒分区
目录 Guava中Lists.partition(List, size) 方法懒划分/懒分区 背景 分析 总结 Guava中Lists.partition(List, size) 方法懒划分/懒分区 ...
- XXE_payload
<?php $xmlfile = file_get_contents('php://input'); $creds=simplexml_load_string($xmlfile); echo $ ...