[Luogu P4178]Tree 题解(点分治+平衡树)
题目大意
给定一棵树,边带权,问有多少点对满足二者间距离$\leq K$,$n \leq 40000$.
题解
点分治专题首杀!$Jackpot!$
(本来看着题意比较简单想捡个软柿子捏,结果手断了……)
点分治的总结先鸽着,这里只说题解。
分析一下题目:
对于无根树上的某一节点x,如果把它看作根,树上的路径无非两类:
1.经过x。
2.不经过x,但在它的子树里。
显然,后者利用点分治的思想经过递归处理可以转化为前者,那么我们就只需考虑第一类,
这也是点分治的强大之处。
我们设$dis[]$为节点到根节点x的距离,那我们要求的就是所有点对$(i,j)$,满足:
$dis[i]+dis[j] \leq K$,且$i,j$不位于同一子树内。
接下来我们想办法实现对$(i,j)$的查询。
我们可以先处理出一棵子树内的$dis[]$,然后对这颗子树进行操作:
对于每个节点u,查询权值前缀$(K-dis[u])$即为所求,可以用类似于桶的树状数组实现。
然后在桶中插入$dis[u]$,表示到跟距离为$dis[u]$的节点数增加1。
按道理来讲,这玩意用树状数组很好搞对叭?
但是我们要插入的权值是$dis[]$,这东西大起来可是完全可以撑爆数组下标的。
(似乎有人拿树状数组水过了?打脸声啪啪啪啪)
不过不要忘了还有一个大家耳熟能详的树锯结垢能实现插入权值、查询前缀和的操作:
平衡树
我们要查询的权值前缀和,不就是相当于查排名么?
之后也就剩点分治的套路了,找根递归处理什么的。
另外还有一些细节:
不要照搬普通平衡树的板子,必须和实际应用相结合。我们这里的查排名可不一定是用平衡树里的权值查,所以有必要先插入再删除。
而且,平衡树查的是不大于某个权值的数的量,不是查排名,所以getrank时没必要ans++。
(相等的也要算啊!是$\leq$不是$<$,所以答案return前要$+cnt[now]$)
不仅如此,你先插入后查询会多一个,最后记得-1。
还有!点分治和平衡树都会有$size[],root$之类的变量,如果设置很多不同的变量名会很麻烦而且容易错,
这时候可以用C++的$namespace$,用法类似于结构体,只不过调用成员方式不是$.$而是$::$,
这样变量名重复就没关系了。
#define R
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=;
#ifdef R
const int L=<<|;
char buffer[L],*S,*T;
#define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)
#endif
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<='')x=x*+ch-'',ch=getchar();
return x*f;
}
namespace Splay
{
int fa[N],cnt[N],son[N][],size[N],key[N],type,root;
void clear(int x)
{
fa[x]=cnt[x]=son[x][]=son[x][]=size[x]=key[x]=;
}
void up(int x)
{
if(x)
{
size[x]=cnt[x];
if(son[x][])size[x]+=size[son[x][]];
if(son[x][])size[x]+=size[son[x][]];
}
}
bool judge(int x)
{
return son[fa[x]][]==x;
}
void rotate(int x)
{
int old=fa[x],oldf=fa[old],lr=judge(x);
son[old][lr]=son[x][lr^];
fa[son[old][lr]]=old;
son[x][lr^]=old;
fa[old]=x;
fa[x]=oldf;
if(oldf)son[oldf][son[oldf][]==old]=x;
up(old);up(x);
}
void splay(int x)
{
for(int f;f=fa[x];rotate(x))
if(fa[f])rotate(judge(x)==judge(f)?f:x);
root=x;
}
void ins(int x)
{
if(!root)
{
type++;
key[type]=x;
root=type;
cnt[type]=size[type]=;
fa[type]=son[type][]=son[type][]=;
return ;
}
int now=root,f=;
while()
{
if(x==key[now])
{
cnt[now]++;
up(now);up(f);
splay(now);
return ;
}
f=now;now=son[now][key[now]<x];
if(!now)
{
type++;
size[type]=cnt[type]=;
son[type][]=son[type][]=;
son[f][x>key[f]]=type;
fa[type]=f;
key[type]=x;
up(f);splay(type);
return ;
}
}
}
int getsum(int x)
{
int now=root,ans=;
while()
{
if(x<key[now])now=son[now][];
else
{
ans+=size[son[now][]];//puts("YOUSA");cout<<x<<' '<<now<<endl;
if(x==key[now])
{
ans+=cnt[now]-;
splay(now);
return ans;
}
ans+=cnt[now];
now=son[now][];
}
}
}
int pre()
{
int now=son[root][];
while(son[now][])now=son[now][];
return now;
}
void del(int x)
{
getsum(x);
if(cnt[root]>)
{
cnt[root]--;
up(root);
return ;
}
if(!son[root][]&&!son[root][])
{
clear(root);root=;
return ;
}
if(!son[root][])
{
int old=root;
root=son[root][];
fa[root]=;
clear(old);
return ;
}
else if(!son[root][])
{
int old=root;
root=son[root][];
fa[root]=;
clear(old);
return ;
}
int old=root,L=pre();
splay(L);
son[root][]=son[old][];
fa[son[old][]]=root;
clear(old);
up(root);
}
}
int n,tot,head[N],len[N<<],nxt[N<<],to[N<<],K;
int size[N],vis[N],maxx,root,sz,ans,dis[N],st[N],top;
void add(int x,int y,int z)
{
to[++tot]=y;
nxt[tot]=head[x];
len[tot]=z;
head[x]=tot;
}
void cacl(int x,int fa)
{
st[++top]=dis[x];
Splay::ins(K-dis[x]);
ans+=Splay::getsum(K-dis[x]);
Splay::del(K-dis[x]);
for(int i=head[x];i;i=nxt[i])
if(!vis[to[i]]&&to[i]!=fa)cacl(to[i],x);
}
void dfs(int x,int fa)
{
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(vis[y]||y==fa)continue;
dis[y]=dis[x]+len[i];
dfs(y,x);
}
}
inline void groot(int x,int fa)
{
size[x]=;
int num=;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==fa||vis[y])continue;
groot(y,x);
size[x]+=size[y];
num=max(num,size[y]);
}
num=max(num,sz-size[x]);
if(maxx>num)maxx=num,root=x;
}
void dac(int x)
{
maxx=0x3f3f3f3f,root=;
groot(x,);
x=root;dis[x]=;vis[x]=;top=;Splay::root=Splay::type=;
dfs(x,);
if(head[x])Splay::ins();
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(vis[y])continue;
cacl(y,x);
if(nxt[i])while(top)Splay::ins(st[top--]);
}
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(vis[y])continue;
sz=size[y];
dac(y);
}
}
int main()
{
n=read();
for(int i=;i<n;i++)
{
int x=read(),y=read(),z=read();
add(x,y,z);add(y,x,z);
}
K=read();sz=n;
dac();
cout<<ans<<endl;
return ;
}
虽然很长但极其无脑
[Luogu P4178]Tree 题解(点分治+平衡树)的更多相关文章
- luogu P4178 Tree
题目链接 luogu P4178 Tree 题解 点分治 代码 // luogu-judger-enable-o2 #include<cstdio> #include<algorit ...
- [Luogu P4178]Tree (点分治+splay)
题面 传送门:https://www.luogu.org/problemnew/show/P4178 Solution 首先,长成这样的题目一定是淀粉质跑不掉了. 考虑到我们不知道K的大小,我们可以开 ...
- P4178 Tree(点分治)
题面要求小于等于K的路径数目,我么很自然的想到点分治(不会的就戳我) 这道题的统计答案与模板题不一样的地方是由等于K到小于等于K 那么我们可以把每一个子节点到当前根(重心)的距离排序,然后用类似双指针 ...
- 2018.07.20 洛谷P4178 Tree(点分治)
传送门 又一道点分治. 直接维护子树内到根的所有路径长度,然后排序+双指针统计答案. 代码如下: #include<bits/stdc++.h> #define N 40005 using ...
- POJ1741:Tree——题解+树分治简要讲解
http://poj.org/problem?id=1741 题目大意:给一棵树,求点对间距离<=k的个数. ———————————————————— 以这道题为例记录一下对于树分治的理解. 树 ...
- 洛谷4178 BZOJ1468 Tree题解点分治
点分治的入门练习. 题目链接 BZOJ的链接(权限题) 关于点分治的思想我就不再重复了,这里重点说一下如何判重. 我们来看上图,假设我们去除了1节点,求出d[2]=1,d[3]=d[4]=2 假设k为 ...
- 【题解】[P4178 Tree]
[题解]P4178 Tree 一道点分治模板好题 不知道是不是我见到的题目太少了,为什么这种题目都是暴力开值域的桶QAQ?? 问点对,考虑点分治吧.直接用值域树状数组开下来,统计的时候直接往树状数组里 ...
- [luogu P3369]【模板】普通平衡树(Treap/SBT)
[luogu P3369][模板]普通平衡树(Treap/SBT) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除x数(若有多个相同的数,因只删 ...
- 【POJ1741】Tree(点分治)
[POJ1741]Tree(点分治) 题面 Vjudge 题目大意: 求树中距离小于\(K\)的点对的数量 题解 完全不觉得点分治了.. 简直\(GG\),更别说动态点分治了... 于是来复习一下. ...
随机推荐
- html生成pdf
/** * 生成pdf * @param string $html 需要生成的内容 */ function pdf($html='<h1 style="color:red"& ...
- iconfont字体图标的使用方法
转载于https://www.cnblogs.com/hjvsdr/p/6639649.html 我之前因为项目用bootstrap比较多,所以使用font awesome字体图标比较多,后来接触到了 ...
- 存储-docker数据共享(13)
容器与 host 共享数据 我们有两种类型的 data volume,它们均可实现在容器与 host 之间共享数据,但方式有所区别. 对于 bind mount 是非常明确的:直接将要共享的目录 mo ...
- ajax中回调的几个坑
在前端开发中,经常要用ajax去拿后台接口返回的数据,总结几个ajax的回调的常见问题,供大家参考爬坑. 未定义contentType,可能会造成的传入后台的数据乱码,可以加上如下代码在ajax请求中 ...
- eclipse debug (调试)基础
进入debug模式: 1.设置断点 2.启动servers端的debug模式 3.运行程序,在后台遇到断点时,进入debug调试状态 ============================= 作用域 ...
- Source Insight下载及注册码
下载地址:http://www.sourceinsight.com/down35.html 注册码: SI3US-205035-36448 SI3US-466908-65897 SI3US-36893 ...
- java.lang.String中的replace方法到底替换了一个还是全部替换了。
你没有看错我说的就是那个最常用的java.lang.String,String可以说在Java中使用量最广泛的类了. 但是我却发现我弄错了他的一个API(也可以说是两个API),这个API是关于字符串 ...
- openlayers中单击获取要素
openlayers中单击获取要素 分类专栏: GIS 总结 OpenLayers 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接: ...
- Linux命令 shutdown
1. 简介 shutdown是用来让计算机处于暂停,关机,重启的指令 2.语法 shutdown [krhHPc] [时间] [警告信息] 时间的格式: hh:mm 表示多长时间以后执行 ...
- VPS性能测试:CPU内存,硬盘IO读写,带宽速度,UnixBench和压力测试
现在便宜的VPS主机越来越多了,一些美国的VPS主机甚至给出1美元一月的VPS,堪比虚拟主机还要便宜,巨大的价格优势吸引不少人购买和使用,而近些年来国内的主机商也开始意识到便宜的VPS对草根站长的诱惑 ...