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_点分治的更多相关文章

  1. [bzoj1468][poj1741]Tree[点分治]

    可以说是点分治第一题,之前那道的点分治只是模模糊糊,做完这道题感觉清楚了很多,点分治可以理解为每次树的重心(这样会把数分为若干棵子树,子树大小为log级别),然后统计包含重心的整个子树的值减去各个子树 ...

  2. POJ1741 Tree + BZOJ1468 Tree 【点分治】

    POJ1741 Tree + BZOJ1468 Tree Description Give a tree with n vertices,each edge has a length(positive ...

  3. poj1741 (点分治)

    Problem Tree 题目大意 给一棵树,有边权.求树上距离小于等于K的点对有多少. 解题分析 点分治.对每一棵子树进行dfs,求出每棵子树的重心,继而转化为子问题. 对于经过根的路径i--j,令 ...

  4. 【BZOJ-1468】Tree 树分治

    1468: Tree Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1025  Solved: 534[Submit][Status][Discuss] ...

  5. poj1741 树上的分治

    题意是说给了n个点的树n<=10000,问有多少个点对例如(a,b)他们的之间的距离小于等于k 采用树的分治做 #include <iostream> #include <cs ...

  6. [POJ1741]Tree(点分治)

    树分治之点分治入门 所谓点分治,就是对于树针对点的分治处理 首先找出重心以保证时间复杂度 然后递归处理所有子树 对于这道题,对于点对(u,v)满足dis(u,v)<=k,分2种情况 路径过当前根 ...

  7. 【BZOJ】1468: Tree(POJ1741) 点分治

    [题意]给定带边权树,求两点距离<=k的点对数.n<=40000. [算法]点分治 [题解]对于一个区域,选择其重心x作为根,则划分出来的每棵子树都是子区域,可以证明至多划分log n次( ...

  8. POJ1741 经典树分治

    题意:有一棵树,每条边有一个距离,求dis(u,v)<=k的点的对数 题解:树分治,对于一颗树上的两点,要么在同一颗子树上,要么在不同子树上,要么一个点是根,另一个在某一子树上,对于第一种情况我 ...

  9. POJ-1741(树分治)

    树的点分治 给出详细的讲解!!点这里打开论文-分治算法在树的路径问题中的应用 本题目是他讲的第一个例题: 我的理解:每次都找树的重心,计算以重心为根的子树之间所贡献的答案.不断这样下去:如果这棵树是一 ...

随机推荐

  1. Android之NDK开发(转载)

    http://www.cnblogs.com/devinzhang/archive/2012/02/29/2373729.html 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C+ ...

  2. MAC地址 初识

    MAC地址 即物理地址/硬件地址 地址长度为48位,6字节. 格式为:00-23-5A-15-99-42 一个网卡对应一个MAC地址(比如笔记本,有线网卡有一个MAC地址,无线网卡也有一个MAC地址) ...

  3. json-server的关系图谱详解(Relationships)

    json-server的关系图谱 json-server是非常好用的一款模拟REST API的工具,文档也很详细和全面.详情:json-server而其中的关系图谱是它非常强大的一个功能,可以非常方便 ...

  4. [Swift通天遁地]八、媒体与动画-(12)CoreText框架中的字体的FontMetrics布局信息

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  5. day01_12/11/2016_Spring入门PPT

    s1 s2 s3 s4 s5 s6 s7 s8 IOC1 IOC2 入门编写1 入门编写2 入门编写3 入门编写4---心得

  6. 342 Power of Four 4的幂

    给定一个整数 (32位有符整数型),请写出一个函数来检验它是否是4的幂.示例:当 num = 16 时 ,返回 true . 当 num = 5时,返回 false.问题进阶:你能不使用循环/递归来解 ...

  7. 12 C#中的方法

    还记得我们的第一个程序吗?忘记了?那你要努力了.我们的第一个程序是就是往dos窗口输出一些字符串.在哪个程序中只有一个方法,Main方法.Main方法是一个特殊的方法,但是它也是一个方法.为什么说Ma ...

  8. STL之vector篇

    #include<iostream> #include<cstdio> #include<cstring> #include<vector> #incl ...

  9. html5——全屏滚动

    鼠标滚轮 window.onmousewheel=function(){}; 基本描述 1.我们使用插件fullpage,为了更好的兼容性 2.动画效果是在滚动到这一屏时触发的,此时给当前屏幕加cur ...

  10. Linux监控实时log

    https://jingyan.baidu.com/article/93f9803f5545a3e0e46f5596.html