(模板)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的源码,但是 ...
随机推荐
- Java Spring MVC工作流程
本文是对 SpringMVC 工作流程的总结,自己一定要可以用语言描述. 名词解释: DispatcherServlet:前端控制器,是 SpringMVC 工作流程的中心,负责调用其他组件,在系统启 ...
- Luogu P4336 [SHOI2016]黑暗前的幻想乡 矩阵树定理+容斥原理
真是菜到爆炸....容斥写反(反正第一次写qwq) 题意:$n-1$个公司,每个公司可以连一些边,求每个边让不同公司连的生成树方案数. 矩阵树定理+容斥原理(注意到$n$不是很大) 枚举公司参与与否的 ...
- C# params object[] args 可以传多个参数,可以不限制类型
http://blog.csdn.net/wslyy99/article/details/1836486 C#参数数组(params)的使用方法 http://www.vcsos.com/articl ...
- QT5 网络通讯
QT5 TCP网络通讯 服务器与客户端建立连接listen() - connectToHost(); 触发newPendingConnect信号 实时数据通讯write(); read(); 触发 ...
- 第四届西安邮电大学acm-icpc校赛 猜球球
题目描述 六一到了,为了庆祝这个节日,好多商家都推出了很多好玩的小游戏.Tongtong看到了一个猜球球的游戏,有n种除了颜色之外完全相同的球,商家从中拿出来一个球球放到了箱子里,已知第i种颜色的球出 ...
- javascript数组的增删改和查询
数组的增删改操作 对数组的增删改操作进行总结,下面(一,二,三)是对数组的增加,修改,删除操作都会改变原来的数组. (一)增加 向末尾增加 push() 返回新增后的数组长度 arr[arr.leng ...
- Java基础_通过模拟售票情景解决线程不安全问题
用代码来模拟铁路售票系统,实现通过四个售票点发售某日某次列车的100张车票,一个售票点用一个线程表示 第一种方法:通过继承Thread类的方法创建线程 package com.Gary1; publi ...
- heartrbeat实现web服务器高可用
今天的内容是用heartbeat实现web服务器高可用 一.简介: heartbeat的工作原理:heartbeat最核心的包括两个部分,心跳监测部分和资源接管部分,心跳监测可以通过网络链路和串口进行 ...
- ZR#710
雷劈数 题意: 现在给出两个整数,求出位于两个整数之间的所有的"雷劈数. 解法: 因为雷劈数特殊的性质,所以在数据范围中的雷劈数实际很少,直接暴力打表就行. CODE: #include&l ...
- javascript操作表单
表单元素除了可以运用上述所有DOM相关操作外,为了简化,还有一系列自己的属性和方法. 表单除了支持鼠标,键盘,更改和html时间之外,还支持一些表单特有的事件,如focus,change,blur等等 ...