(模板)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的源码,但是 ...
随机推荐
- kubernetes 存储volume,pv和pvc的使用
emptyDIR 临时目录 hostPath :使用主机的路径 网络存储: 传统的设备存储:NAS,SAN 分布式存储:glusterfs,rbd,cephfs 云存储:EBS,Azure,阿里云的 ...
- 【CUDA 基础】4.5 使用统一内存的向量加法
title: [CUDA 基础]4.5 使用统一内存的向量加法 categories: - CUDA - Freshman tags: - 统一内存 - Uniform Memory toc: tru ...
- delphi将两个Strlist合并,求交集 (保留相同的)
Function StrList_Join(StrListA,StrListB:String):String; //将两个Strlist合并,求交集 (保留相同的) var SListA,SListB ...
- neo4j︱与python结合的py2neo使用教程
—- 目前的几篇相关:—– neo4j︱图数据库基本概念.操作罗列与整理(一) neo4j︱Cypher 查询语言简单案例(二) neo4j︱Cypher完整案例csv导入.关系联通.高级查询(三) ...
- Jury Compromise
K - Jury Compromise 参考:ACM POJ 1015 Jury Compromise(陪审团的人选,动态规划题,难) 说实话真有点难想,用一个DP[i][j]来表示在选取i个人,辩控 ...
- Java中局部变量、实例变量和静态变量在方法区、栈内存、堆内存中的分配
转自:https://blog.csdn.net/leunging/article/details/80599282 感谢CSDN博主「leunging」的总结分享 ———————————————— ...
- 【CF671D】 Roads in Yusland(对偶问题,左偏树)
传送门 洛谷翻译 CodeForces Solution emmm,先引入一个对偶问题的概念 \(max(c^Tx|Ax \leq b)=min(b^Ty|A^Ty \ge c)\) 考虑这个式子的现 ...
- python 快速排序-代码示例
def quick_sort(alist, first, last): if first >= last: # 如果开始等于结尾,即就一个元素 return mid_value = alist[ ...
- C#_选择结构,Console的应用,数据类型转换
1:先看一个顺序结构编程,代码如下: using System; using System.Collections.Generic; using System.Linq; using System.T ...
- VMware配置NAT方式下的静态ip
一.VMware上NAT模式工作原理 原理图如下: 说明: 1.虚拟主机与本地主机通信时,直接通过虚拟交换机访问(不管是虚拟主机的ip是静态ip还是动态分配的ip) 2.虚拟主机与外网通信时,虚拟主机 ...