【BZOJ3124】[Sdoi2013]直径

Description

小Q最近学习了一些图论知识。根据课本,有如下定义。树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度。如果一棵树有N个节点,可以证明其有且仅有N-1 条边。 路径:一棵树上,任意两个节点之间最多有一条简单路径。我们用 dis(a,b)
表示点a和点b的路径上各边长度之和。称dis(a,b)为a、b两个节点间的距离。  
 直径:一棵树上,最长的路径为树的直径。树的直径可能不是唯一的。 
现在小Q想知道,对于给定的一棵树,其直径的长度是多少,以及有多少条边满足所有的直径都经过该边。

Input

第一行包含一个整数N,表示节点数。 
接下来N-1行,每行三个整数a, b, c ,表示点 a和点b之间有一条长度为c
的无向边。

Output

共两行。第一行一个整数,表示直径的长度。第二行一个整数,表示被所有
直径经过的边的数量。

Sample Input

6
3 1 1000
1 4 10
4 2 100
4 5 50
4 6 100

Sample Output

1110
2
【样例说明】
直径共有两条,3 到2的路径和3到6的路径。这两条直径都经过边(3, 1)和边(1, 4)。

HINT

对于100%的测试数据:2≤N≤200000,所有点的编号都在1..N的范围内,
边的权值≤10^9。

题解:网上都说这题是结论题,吓傻了~

第一问随便求,直接说第二问。

首先,正难则反,统计被所有直径都经过的边有点困难,我们可以统计那些边被至少一条直径错过。我们在求直径的时候可以维护每个点子树中深度的最大值和次大值(二者不再同一个儿子的子树中),如果最大值和次大值组合起来刚好是一条直径,那么不再这两个子树中的所有边就都一定错过了,我们在DFS序上打一个标记即可(用差分)。但是有一个问题,最大值和次大值组合起来不一定是唯一的直径,可能最大值和次次大值也能组合起来成为直径,还有次次次。。。。不过我们有必要把这些都记录下来吗?显然没有,我们只需要记录最大,次大,次次大即可,这3个儿子的组合已经足以“淘汰”掉剩余的所有儿子。(这里需要自己思考一下)

但是这还不够,我们刚才讨论只是直径上的最高点,还要继续考虑下面的边。这里需要记录h[x]代表x子树外的点到x的最远距离,如果x子树内的深度最大值和h[x]能组成一条直径,那么我们就可以将x的其他儿子全都淘汰掉。但是同上,我们还需要看一下最大值也是否应该被淘汰掉,所以用h[x]和次大值组合一下就行了。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn=200010;
int n,cnt,ans;
int to[maxn<<1],next[maxn<<1],head[maxn],fa[maxn],p[maxn],q[maxn],Q[maxn];
ll len,val[maxn<<1],dep[maxn],lf[maxn],g[maxn][3],h[maxn],s[maxn];
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar();
return ret*f;
}
inline void add(int a,int b,int c)
{
to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
inline void pushup(int x,int y)
{
if(lf[y]>lf[g[x][0]]) g[x][2]=g[x][1],g[x][1]=g[x][0],g[x][0]=y;
else if(lf[y]>lf[g[x][1]]) g[x][2]=g[x][1],g[x][1]=y;
else if(lf[y]>lf[g[x][2]]) g[x][2]=y;
}
void dfs(int x)
{
p[x]=++Q[0],Q[Q[0]]=x;
lf[x]=dep[x];
for(int i=head[x],y;i!=-1;i=next[i]) if(to[i]!=fa[x])
{
y=to[i],fa[y]=x,dep[y]=dep[x]+val[i],dfs(y),lf[x]=max(lf[x],lf[y]),pushup(x,y);
}
len=max(len,lf[g[x][0]]+lf[g[x][1]]-2*dep[x]);
q[x]=Q[0];
}
inline void sumup(int a,int b)
{
if(a<=b) s[a]++,s[b+1]--;
}
inline void updata(int x,int a,int b)
{
sumup(1,p[x]),sumup(q[x]+1,n);
if(p[a]>p[b]) swap(a,b);
sumup(p[x]+1,p[a]-1),sumup(q[a]+1,p[b]-1),sumup(q[b]+1,q[x]);
}
int main()
{
n=rd();
int i,x,y,a,b,c;
memset(head,-1,sizeof(head));
for(i=1;i<n;i++) a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c);
dfs(1);
for(i=1;i<=n;i++)
{
x=Q[i],y=fa[x];
if(lf[g[x][0]]+lf[g[x][1]]-2*dep[x]==len) updata(x,g[x][0],g[x][1]);
if(lf[g[x][0]]+lf[g[x][2]]-2*dep[x]==len) updata(x,g[x][0],g[x][2]);
if(lf[g[x][1]]+lf[g[x][2]]-2*dep[x]==len) updata(x,g[x][1],g[x][2]);
if(x==g[y][0]) h[x]=max(h[y],lf[g[y][1]]-dep[y]*2);
else h[x]=max(h[y],lf[g[y][0]]-dep[y]*2);
if(h[x]+lf[x]!=len) s[p[x]]++,s[p[x]+1]--;
if(h[x]+lf[g[x][0]]==len) sumup(p[x]+1,p[g[x][0]]-1),sumup(q[g[x][0]]+1,q[x]);
if(h[x]+lf[g[x][1]]==len) sumup(p[x]+1,p[g[x][1]]-1),sumup(q[g[x][1]]+1,q[x]);
}
for(i=1;i<=n;i++) s[i]+=s[i-1],ans+=(i!=1)&&(!s[i]);
printf("%lld\n%d",len,ans);
return 0;
}

【BZOJ3124】[Sdoi2013]直径 树形DP(不用结论)的更多相关文章

  1. bzoj3124: [Sdoi2013]直径 树形dp two points

    题目链接 bzoj3124: [Sdoi2013]直径 题解 发现所有直径都经过的边 一定在一条直径上,并且是连续的 在一条直径上找这段区间的两个就好了 代码 #include<map> ...

  2. BZOJ3124: [Sdoi2013]直径 (树形DP)

    题意:给一颗树 第一问求直径 第二问求有多少条边是所有直径都含有的 题解:求直径就不说了 解第二问需要自己摸索出一些性质 任意记录一条直径后 跑这条直径的每一个点  如果以这个点不经过直径能到达最远的 ...

  3. [SDOI2013] 直径 - 树形dp

    对于给定的一棵树,其直径的长度是多少,以及有多少条边满足所有的直径都经过该边. Solution 有点意思 先随便求一条直径(两次DFS即可),不妨设为 \(s,t\),我们知道要求的这些边一定都在这 ...

  4. 2014 Super Training #9 E Destroy --树的直径+树形DP

    原题: ZOJ 3684 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3684 题意: 给你一棵树,树的根是树的中心(到其 ...

  5. 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分

    树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...

  6. [10.12模拟赛] 老大 (二分/树的直径/树形dp)

    [10.12模拟赛] 老大 题目描述 因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n − 1 条边的无向连通图), ...

  7. Codeforces 633F 树的直径/树形DP

    题意:有两个小孩玩游戏,每个小孩可以选择一个起始点,并且下一个选择的点必须和自己选择的上一个点相邻,问两个选的点权和的最大值是多少? 思路:首先这个问题可以转化为求树上两不相交路径的点权和的最大值,对 ...

  8. bzoj千题计划134:bzoj3124: [Sdoi2013]直径

    http://www.lydsy.com/JudgeOnline/problem.php?id=3124 第一问: dfs1.dfs2 dfs2中记录dis[i]表示点i距离最长链左端点的距离 第二问 ...

  9. 2018.11.05 bzoj3124: [Sdoi2013]直径(树形dp)

    传送门 一道sbsbsb树形dpdpdp 第一问直接求树的直径. 考虑第二问问的边肯定在同一条直径上均是连续的. 因此我们将直径记下来. 然后对于直径上的每一个点,dpdpdp出以这个点为根的子树中不 ...

随机推荐

  1. 倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-电机实际运行距离跟给定距离不一致怎么办,如何设置Scaling Factor

    有时候,让电机从0度转到绝对的360度,有时候会出现电机实际转动更多或者更少的情况.   一般是电机的编码器的Scaling Factor Numerator数值不对导致的,数值越小,则同比转过角度越 ...

  2. 游戏AI的综合设计

    原地址:http://www.cnblogs.com/cocoaleaves/archive/2009/03/23/1419346.html 学校的MSTC要出杂志,第一期做游戏专题,我写了一下AI, ...

  3. Qt学习 之 多线程程序设计

    QT通过三种形式提供了对线程的支持.它们各自是, 一.平台无关的线程类 二.线程安全的事件投递 三.跨线程的信号-槽连接. 这使得开发轻巧的多线程Qt程序更为easy,并能充分利用多处理器机器的优势. ...

  4. SSH 基于ajax实现修改密码功能步骤梳理

    1. 为密码输入框进行输入校验,使用easyUI提供的easyui-validatebox <table cellpadding=3> <tr> <td>新密码:& ...

  5. MySQL 5.6数据导入报 GTID 相关错误

    从阿里云备份数据后还原到本地,用命令行 mysql -uroot -p --default-character-set=<character> -f <dbname> < ...

  6. H5页面在IOS下不会自动播放音乐的坑

    document.addEventListener(‘DOMContentLoaded‘, function () { function audioAutoPlay() { var audio = d ...

  7. 基于Virtext6平台的GTX IP核基本设置说明

    本工程基于以下条件使用: 板卡:DBF板v3.0 芯片型号:Virtex6 315T ISE版本:14.7 IP核版本: v6_gtxwizard : 1.12 一.IP核配置进行流程 第一页配置:线 ...

  8. 游戏引擎 Unity 的入门易精通难体现在哪?为什么?

    04月212014年   [王楠的回答(37票)]: 为什么入门简单,看一下官网的文档和视频教程就知道了,看完几段视频和例子就能让初学者做出能玩的东西,其他同类商业引擎都做不到.物体+组件的结构,所见 ...

  9. [c#.Net]正则表达式 记录

    @符号c#字符串前使用@符号,“@”表示,跟在它后面的字符串是个“逐字字符串”. string x="D:\\My Huang\\My Doc"; string y = @&quo ...

  10. Eclipse开发C/C++之使用技巧小结,写给新手

    我需要在Linux下开发C++项目,没有VS,用Vim开发是不错,但项目大了,效率 就跟不上IDE了,所以选了Eclipse+CDT插件.当然,Vimers觉得我说的不对的请 勿喷哈,我也是水手一个. ...