(模板)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的源码,但是 ...
随机推荐
- 关于kafka定期清理日志后再消费报错kafka.common.OffsetOutOfRangeException的解决
环境: kafka 0.10 spark 2.1.0 zookeeper 3.4.5-cdh5.14.0 公司阿里云测试机,十月一放假前,没有在继续消费,假期过后回来再使用spark strea ...
- sql语句中where 1=1和 0=1 的作用
sql where 1=1和 0=1 的作用 where 1=1; 这个条件始终为True,在不定数量查询条件情况下,1=1可以很方便的规范语句. 一.不用where 1=1 在多条件查询中的 ...
- Nginx-HTTP之listen指令解析
1. ngx_http_core_listen static char * ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void ...
- 黑马vue---16、vue中通过属性绑定为元素设置class类样式
黑马vue---16.vue中通过属性绑定为元素设置class类样式 一.总结 一句话总结: 这里就是为元素绑定class样式,和后面的style样式区别一下 vue中class样式绑定方式的相对于原 ...
- 空指针/0/NULL
空指针/0/NULL 空指针是一个被赋值为0的指针,在没有被具体初始化之前,其值为0. NULL 是一个标准规定的宏定义,用来表示空指针常量. #define NULL 0 或者 #define ...
- 【JDBC】使用Spring提供的JDBCTemplate通过Statement向MySql数据库插入千万条数据,耗时4m55s,使用insert语句批量插入方式二
这回依然是使用 insert批量插入这种方式 insert into emp(name,age,cdate) values ('A' , 20, '2019-10-13 00:00:00'), ('B ...
- 相似系数_杰卡德距离(Jaccard Distance)
python机器学习-乳腺癌细胞挖掘(博主亲自录制视频)https://study.163.com/course/introduction.htm?courseId=1005269003&ut ...
- 使用git send-email发送邮件时报错: Unable to initialize SMTP properly怎么处理?
答: 配置~/.gitconfig中的smtpserver 需往~/.gitconfig中添加如下内容: [sendemail] smtpserver = <stmp_server_name ...
- leetcode16 最接近的三数之和
做了几周的hard之后,这道题居然轻易就解出来了,稍微debug了一下就ac了,算是有了一丢丢提高把: 思路 这道题因为和三数之和很像,所以充分利用双指针的思想:先排序,然后再固定一个数i,i取值从[ ...
- [面试] Java高级软件工程师面试考纲(转)
如果要应聘高级开发工程师职务,仅仅懂得Java的基础知识是远远不够的,还必须懂得常用数据结构.算法.网络.操作系统等知识.因此本文不会讲解具体的技术,笔者综合自己应聘各大公司的经历,整理了一份大公司对 ...