题目大意

给定一棵树,边带权,问有多少点对满足二者间距离$\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 题解(点分治+平衡树)的更多相关文章

  1. luogu P4178 Tree

    题目链接 luogu P4178 Tree 题解 点分治 代码 // luogu-judger-enable-o2 #include<cstdio> #include<algorit ...

  2. [Luogu P4178]Tree (点分治+splay)

    题面 传送门:https://www.luogu.org/problemnew/show/P4178 Solution 首先,长成这样的题目一定是淀粉质跑不掉了. 考虑到我们不知道K的大小,我们可以开 ...

  3. P4178 Tree(点分治)

    题面要求小于等于K的路径数目,我么很自然的想到点分治(不会的就戳我) 这道题的统计答案与模板题不一样的地方是由等于K到小于等于K 那么我们可以把每一个子节点到当前根(重心)的距离排序,然后用类似双指针 ...

  4. 2018.07.20 洛谷P4178 Tree(点分治)

    传送门 又一道点分治. 直接维护子树内到根的所有路径长度,然后排序+双指针统计答案. 代码如下: #include<bits/stdc++.h> #define N 40005 using ...

  5. POJ1741:Tree——题解+树分治简要讲解

    http://poj.org/problem?id=1741 题目大意:给一棵树,求点对间距离<=k的个数. ———————————————————— 以这道题为例记录一下对于树分治的理解. 树 ...

  6. 洛谷4178 BZOJ1468 Tree题解点分治

    点分治的入门练习. 题目链接 BZOJ的链接(权限题) 关于点分治的思想我就不再重复了,这里重点说一下如何判重. 我们来看上图,假设我们去除了1节点,求出d[2]=1,d[3]=d[4]=2 假设k为 ...

  7. 【题解】[P4178 Tree]

    [题解]P4178 Tree 一道点分治模板好题 不知道是不是我见到的题目太少了,为什么这种题目都是暴力开值域的桶QAQ?? 问点对,考虑点分治吧.直接用值域树状数组开下来,统计的时候直接往树状数组里 ...

  8. [luogu P3369]【模板】普通平衡树(Treap/SBT)

    [luogu P3369][模板]普通平衡树(Treap/SBT) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除x数(若有多个相同的数,因只删 ...

  9. 【POJ1741】Tree(点分治)

    [POJ1741]Tree(点分治) 题面 Vjudge 题目大意: 求树中距离小于\(K\)的点对的数量 题解 完全不觉得点分治了.. 简直\(GG\),更别说动态点分治了... 于是来复习一下. ...

随机推荐

  1. 【软工项目Beta阶段】第10周Scrum会议博客

    第十周会议记录 小组GitHub项目地址https://github.com/ouc-softwareclass/OUC-Market 小组Issue地址https://github.com/ouc- ...

  2. linux日常---1、linux下安装、查看、卸载包常用命令

    linux日常---1.linux下安装.查看.卸载包常用命令 一.总结 一句话总结: 对比学习 1.linux如何查看系统中安装的程序? rpm -qa   # 查看所有安装的软件包 2.linux ...

  3. PHP面试 PHP基础知识 十一(开发环境及相关配置)

    开发环境及相关配置 版本控制软件 集中式和分布式 集中式:集中式版本控制工具,版本库集中存放在中央服务器,团队成员里的每个人工作时从中央服务器下载代码,个人修改后再提交到中央服务器 分布式:分布式版本 ...

  4. Java学习之抽象类

    抽象类特点: 1.抽象方法必须定义在抽象类中2.abstract关键字修饰:只能修饰类和方法3.抽象类不能实例化4.抽象类中的方法要被使用,必须由子类重写所有的抽象方法,实例化其子类 如果子类只重写部 ...

  5. javaScript 通过location对象获取项目的url

    项目中有些要通过jQuery 动态加载,其中需要一些路径,使用相对路径会出现错误,报 $("#t1").html("设置或返回从井号 (#) 开始的 URL(锚)---& ...

  6. ORACLE切非归档模式:

    C:\Documents and Settings\Administrator>sqlplus /nologSQL> conn / as sysdbaConnected to an idl ...

  7. windows下Docker安装MySQL

    # docker 中下载 mysql docker pull mysql #启动 docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD ...

  8. PAT甲级——A1140 LookAndSaySequence【20】

    Look-and-say sequence is a sequence of integers as the following: D, D1, D111, D113, D11231, D112213 ...

  9. docker对容器进行资源限制

    对内存进行资源限制 docker run --memory=200m soymilk/stress 对cpu进行资源限制 终端1 docker run --name=test1 --cpu-share ...

  10. 发布 Vant - 高效的 Vue 组件库,再造一个有赞移动商城也不在话下

    发布 Vant - 高效的 Vue 组件库,再造一个有赞移动商城也不在话下:https://segmentfault.com/a/1190000011377961 vantUI框架在vue项目中的应用 ...