[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\),更别说动态点分治了... 于是来复习一下. ...
随机推荐
- 【Flutter学习】页面跳转之SliverAppBar,CustomScrollView,NestedScrollView的使用
一,flutter SliverAppbar 控件介绍 SliverAppBar “应用栏” 相当于升级版的 appbar 于 AppBar 位置的固定的应用最上面的; 而 SliverAppBar ...
- 旋转屏幕导致Activity重建问题的解决办法
Android开发文档上专门有一小节解释这个问题.简单来说,Activity是负责与用户交互的最主要机制,任何"设置"(Configuration)的改变都可能对Activity的 ...
- Android中软键盘弹出时关于布局的问题
当在Android的layout设计里面如果输入框过多,则在输入弹出软键盘的时候,下面的输入框会有一部分被软件盘挡住,从而不能获取焦点输入. 解决办法: 方法一:在你的activity中的oncre ...
- CDN技术之--集群服务与负载均衡
Web集群是由多个同时运行同一个web应用的服务器组成,在外界看来就像一个服务器一样,这多台服务器共同来为客户提供更高性能的服务.集群更标准的定义是:一组相互独立的服务器在网络中表现为单一的系统,并以 ...
- rocketmq单点部署
下载地址:https://github.com/alibaba/RocketMQ 转载请注明来源:http://blog.csdn.net/loongshawn/article/details/510 ...
- [MySQL] innobackupex在线备份及恢复(全量和增量)
安装percona-xtrabackup 方法1: percona-xtrabackup-2.1.9-744-Linux-x86_64.tar.gz(D:\share\src\linux-mysql) ...
- js与android原生交互
package com.liuhao.mysecond; import androidx.annotation.RequiresApi;import androidx.appcompat.app.Ap ...
- 【Java架构:基础技术】一篇文章搞掂:MyBatis
本文篇幅较长,建议合理利用右上角目录进行查看(如果没有目录请刷新). 本文主要总结于刘增辉的<MyBatisc从入门到精通>一书,有兴趣的朋友可以自行研读 建议仔细研读官方文档: http ...
- (转)Maven创建webapp项目无法修改web版本的问题
maven创建的web app,默认使用的servlet版本是2.3,默认不支持JSTL,为了默认支持JSTL表达式,需要升级servlet到3.0 转:http://blog.sina.com.cn ...
- python selenium模拟登陆163邮箱。
selenium是可以模拟浏览器操作. 有些爬虫是异步加载的,通过爬取网页源码是得不到需要的内容.所以可以模拟浏览器去登陆该网站进行爬取操作. 需要安装selenium通过pip install xx ...