[bzoj1468][poj1741]Tree_点分治
Tree bzoj-1468 poj-1741
题目大意:给你一颗n个点的树,求树上所有路径边权和不大于m的路径条数。
注释:$1\le n\le 4\cdot 10^4$,$1\le m \le 10^9$。
想法:GXZlegend给高一讲点分治,去听了之后的第一道模板题。
我们对于一类树上统计问题,除了强大的树形dp之外,我们还有分治。今天听的是点分治:
就是说,我们将所有的链关于一个点分划成两类:过这个点的链,和不过这个点的链。这个点就是根节点,我在任意两颗子树中拎出两个点,他们之间的链,就是经过根节点的链。紧接着,我们递归处理这个过程。对于每一个子树,钦定一个根节点,然后求这个子树中经过子树的钦定节点且满足条件的链。那么,这个钦定节点如何选取?显然,树链统计问题可以O(n*n)枚举所有链,那么,我要使得这样的钦定节点可以降低枚举复杂度。于是,我在递归时要尽量使得所有的子树尽量差不多,这样时间复杂度会降下来。故,我们可以钦定树的重心,这样每次递归都求重心,时间复杂度为O(n*logn)。每一次递归的时候重新更新所有节点的所有信息,把当过重心的节点通过mark的方式删掉,就完成了点分治的过程。
剩下的,就是一些代码:
找重心的时候顺便更新当前子树的size
void getroot(int pos,int fa)
//与其函数名叫做getroot,倒不如数get_original,因为每一次递归都必须求重心和节点size
{
f[pos]=0;
size[pos]=1;
for(int i=head[pos];i;i=nxt[i])
{
if(to[i]==fa||vis[to[i]]) continue;
getroot(to[i],pos);
size[pos]+=size[to[i]];
f[pos]=max(f[pos],size[to[i]]);
}
f[pos]=max(f[pos],sn-size[pos]);
if(f[root]>f[pos]) root=pos;
}
回归本题,我们期望寻找到所有的链,那么我就可以在钦定完节点之后,从所有子树的所有节点中拎出所有节点的deep,我只需要找到这些deep之间和小于等于m的(先不考虑同一颗子树中的情况)。找出这些deep之后,用双指针即可求出当前链假装过重心(同一颗子树有算重的情况)的个数,然后运用容斥原理,减掉每一颗子树中的情况即可。
由于双指针的时候需要排序,所以总的之间复杂度是$O(n\cdot log^2n)$
最后,附上丑陋的代码... ...
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 40010
using namespace std;
int to[N<<1],head[N],cnt,nxt[N<<1],val[N<<1];
int f[N],root,m,deep[N],size[N],sn,d[N],tot,ans;
bool vis[N];
inline void add(int x,int y,int z)
{//用cnt是因为后面顺手写了tot
to[++cnt]=y;
val[cnt]=z;
nxt[cnt]=head[x];
head[x]=cnt;
}
void getroot(int pos,int fa)
//与其函数名叫做getroot,倒不如数get_original,因为每一次递归都必须求重心和节点size
{
f[pos]=0;
size[pos]=1;
for(int i=head[pos];i;i=nxt[i])
{
if(to[i]==fa||vis[to[i]]) continue;
getroot(to[i],pos);
size[pos]+=size[to[i]];
f[pos]=max(f[pos],size[to[i]]);
}
f[pos]=max(f[pos],sn-size[pos]);
if(f[root]>f[pos]) root=pos;
}
void getdeep(int pos,int fa)//deep是当前节点到重心的路径边权和
{
d[++tot]=deep[pos];//之后需要双指针
for(int i=head[pos];i;i=nxt[i])
{
if(to[i]==fa||vis[to[i]]) continue;
deep[to[i]]=deep[pos]+val[i],getdeep(to[i],pos);
}
}
int calc(int pos)//双指针求过pos的满足条件数
{
tot=0;
getdeep(pos,0);
sort(d+1,d+tot+1);
int i=1,j=tot,sum=0;
while(i<j)
{
if(d[i]+d[j]<=m) sum+=j-i,i++;
else j--;
}
return sum;
}
void dfs(int pos)
{
deep[pos]=0;
vis[pos]=1;
ans+=calc(pos);
for(int i=head[pos];i;i=nxt[i])
{
if(!vis[to[i]])
{
deep[to[i]]=val[i];
ans-=calc(to[i]);//单步容斥
sn=size[to[i]];//求重心时用得到
root=0;//当前子树重心root
getroot(to[i],0);
//现在root是重心了
dfs(root);
}
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);add(y,x,z);
}
cnt=0,ans=0;
scanf("%d",&m);
f[0]=0x7f7f7f7f;//绝对不能让0是root的神奇操作qwq
sn=n;
root=0,getroot(1,0),dfs(root);
printf("%d\n",ans);
return 0;
}
小结:错误在于对点分治理解不够深刻(其实是双指针的时候j++导致全盘爆炸).
点分治是处理树上统计问题的好方法。鸣谢GXZlegend
[bzoj1468][poj1741]Tree_点分治的更多相关文章
- [bzoj1468][poj1741]Tree[点分治]
可以说是点分治第一题,之前那道的点分治只是模模糊糊,做完这道题感觉清楚了很多,点分治可以理解为每次树的重心(这样会把数分为若干棵子树,子树大小为log级别),然后统计包含重心的整个子树的值减去各个子树 ...
- POJ1741 Tree + BZOJ1468 Tree 【点分治】
POJ1741 Tree + BZOJ1468 Tree Description Give a tree with n vertices,each edge has a length(positive ...
- poj1741 (点分治)
Problem Tree 题目大意 给一棵树,有边权.求树上距离小于等于K的点对有多少. 解题分析 点分治.对每一棵子树进行dfs,求出每棵子树的重心,继而转化为子问题. 对于经过根的路径i--j,令 ...
- 【BZOJ-1468】Tree 树分治
1468: Tree Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1025 Solved: 534[Submit][Status][Discuss] ...
- poj1741 树上的分治
题意是说给了n个点的树n<=10000,问有多少个点对例如(a,b)他们的之间的距离小于等于k 采用树的分治做 #include <iostream> #include <cs ...
- [POJ1741]Tree(点分治)
树分治之点分治入门 所谓点分治,就是对于树针对点的分治处理 首先找出重心以保证时间复杂度 然后递归处理所有子树 对于这道题,对于点对(u,v)满足dis(u,v)<=k,分2种情况 路径过当前根 ...
- 【BZOJ】1468: Tree(POJ1741) 点分治
[题意]给定带边权树,求两点距离<=k的点对数.n<=40000. [算法]点分治 [题解]对于一个区域,选择其重心x作为根,则划分出来的每棵子树都是子区域,可以证明至多划分log n次( ...
- POJ1741 经典树分治
题意:有一棵树,每条边有一个距离,求dis(u,v)<=k的点的对数 题解:树分治,对于一颗树上的两点,要么在同一颗子树上,要么在不同子树上,要么一个点是根,另一个在某一子树上,对于第一种情况我 ...
- POJ-1741(树分治)
树的点分治 给出详细的讲解!!点这里打开论文-分治算法在树的路径问题中的应用 本题目是他讲的第一个例题: 我的理解:每次都找树的重心,计算以重心为根的子树之间所贡献的答案.不断这样下去:如果这棵树是一 ...
随机推荐
- C#窗体间传值的简便方法/工具
一.问题:窗体间传值必须需要窗体之间有联系,具体有如下方式 窗体间传值涉及到窗体A必须拥有窗体B,这样才可以实现A-B之间传值 窗体A与窗体B在窗体/实例C中,A-B可互相通讯 其他方式,不细讨论,复 ...
- frameset的target属性
使用frameset时的target属性 (2012-09-18 08:19:31) 转载▼ 分类: java技术之路 一般常用的有四个属性 _blank 浏览器总在一个新打开.未命名的窗口中载入 ...
- 使用内存流导出Excel
public MemoryStream MemoryStreamDeiveFlowInfoaging(DataTable Table) { Dictionary<int , string> ...
- [App Store Connect帮助]三、管理 App 和版本(2.4)输入 App 信息:提供加密出口合规证明文稿
上传至 App Store Connect 的 App 被上传至位于美国的 Apple 服务器.如果您提交 App 的目的是为了在 App Store 上分发您的 App 或通过美国或加拿大的境外 T ...
- 关于网页的自适应问题一---Media Query(媒介查询)
1.Media Query(媒介查询) 通过不同的媒介类型和条件定义样式表规则.媒介查询让CSS可以更精确作用于不同的媒介类型和同一媒介的不同条件.媒介查询的大部分媒介特性都接受min和max用于表达 ...
- Android内存管理(12)*「实例」用Monitor 生成.hprof文件 并分析内存泄漏
参考 http://blog.csdn.net/xiaanming/article/details/42396507 基本步骤: 1,准备一个有内存泄漏的代码 2,如何发现内存泄漏 3,生成.hpro ...
- ansible 显示运行时间
#独家秘诀cd /etc/ansible mkdir callback_plugins cd callback_plugins wget https://raw.githubusercontent.c ...
- CSS布局——三栏布局
说到三栏布局,很多都会提到圣杯布局和双飞翼布局这两个经典的三栏布局方式.于是,我在网上搜了一些相关资料,阅读并跟着代码敲了一遍,发现在处理三栏布局上,他们采用的都是两边栏固定,中间栏自适应的策略.在处 ...
- cordova科大讯飞语音识别
cordova-plugin-IFlyspeech 科大讯飞的语音听说读写的cordova插件 Supported Platforms iOS android Installation 插件安装命令: ...
- [Android]异常4-javax.mail.AuthenticationFailedException
javax.mail.AuthenticationFailedException 背景:JavaMail发送电子邮件 异常原因: 可能一>发件人帐号.密码有误 可能二>需要使用SSL安全认 ...