BZOJ 2282 & 树的直径

SDOI2011的Dayx第2题

题意:

  在树中找到一条权值和不超过S的链(为什么是链呢,因为题目中提到“使得路径的两端都是城市”,如果不是链那不就不止两端了吗——怎么这么机智的感觉...),使得不在链上的点与这条链的距离最大值最小。

SOL:

  最大值最小!这不是二分的节奏么?然而hzw学长说二分更直观我却一点都没有体会到...

  这道题的关键是猜想(貌似还挺好想)并证明(貌似一直都是可有可无的东西,不过还挺好证的),路径一定在直径上,那么我们先两遍*FS找到直径,用一个队列维护链上的路径,以及预处理出直径上的点其子树中距离最远的点的距离,再维护路径两头距离直径的两端的距离即可,答案即这三者的最大值。

  总感觉应该挺快的吧但是居然比他们二分答案的慢了几倍(A得窝囊),我也就是用了一个set而已(方便取出与删除然而我觉得可以再优化不用这个set...

  关于证明:(自己口胡的证明,极其不严谨

    证明还是非常好证的(也就自己yy),我们假设一条直径,假设最优的链不在这条直径上——我们可以把这条链看作一个点,那么可以证明它的最远点一定是直径的两个端点之一(否则树的直径就可以更长),那么显然这条链是包含直径的一部分的,再证全部包含,只要在部分包含的基础上对延伸进行讨论,就得证了吧...(感觉对了就好了不过是给自己a掉多找一个正当理由...)

CODE:

代码实现不是很难,然而我RE几次方了把DFS改成BFS又把自带queue改成手工队列——结果小细节错误百出...

  1 /*==========================================================================
  2 # Last modified: 2016-02-12
  3 # Filename: 2016trainning1 t1.cpp
  4 # Description:
  5 ==========================================================================*/
  6 #define me AcrossTheSky
  7 #include <cstdio>
  8 #include <cmath>
  9 #include <ctime>
 10 #include <string>
 11 #include <cstring>
 12 #include <cstdlib>
 13 #include <iostream>
 14 #include <algorithm>
 15
 16 #include <set>
 17 #include <map>
 18 #include <stack>
 19 #include <queue>
 20 #include <vector>
 21 #define lowbit(x) (x)&(-x)
 22 #define INF 1000000000
 23 #define FOR(i,a,b) for((i)=(a);(i)<=(b);(i)++)
 24 #define FORP(i,a,b) for(int i=(a);i<=(b);i++)
 25 #define FORM(i,a,b) for(int i=(a);i>=(b);i--)
 26 #define ls(a,b) (((a)+(b)) << 1)
 27 #define rs(a,b) (((a)+(b)) >> 1)
 28 #define check(ch) (ch>=‘0‘&&ch<=‘9‘)
 29 #define maxn 300001
 30 using namespace std;
 31 typedef long long ll;
 32 typedef unsigned long long ull;
 33 int ans=INF;
 34 void read(int &t){
 35     int ch;
 36     for(ch=getchar( );!check(ch);ch=getchar( ));
 37     t=ch-‘0‘;
 38     for(ch=getchar( );check(ch);ch=getchar( ))t*=10,t+=ch-‘0‘;
 39 }
 40 /*==================split line==================*/
 41 int n,s,sume=0,maxx;
 42 struct Edge{
 43     int to,len;
 44 }e[maxn*2];
 45 struct cmp{
 46     bool operator()(const int &x,const int &y) const{
 47         return x>y;
 48     }
 49 };
 50 int first[maxn],next[maxn*2],nextn[maxn],dis[maxn],temp[2],f[maxn],pre[maxn];
 51 int q[maxn*2];
 52 bool vis[maxn];
 53 set<int,cmp> b;
 54 void addedge(int x,int y,int len){
 55     sume++; e[sume].to=y; e[sume].len=len;
 56     next[sume]=first[x]; first[x]=sume;
 57 }
 58 void bfs(int node,int d){
 59     memset(q,0,sizeof(q));
 60     int front=1,tail=1; dis[node]=0;
 61     q[front]=node; vis[node]=true;
 62     while (front<=tail){
 63         int x=q[front];
 64         front++;
 65         if (dis[x]>maxx) maxx=dis[x],temp[d]=x;
 66         for (int i=first[x];i!=-1;i=next[i])
 67         if (!vis[e[i].to]){
 68             pre[e[i].to]=x;
 69             dis[e[i].to]=dis[x]+e[i].len;
 70             tail++; q[tail]=e[i].to; vis[e[i].to]=true;
 71         }
 72     }
 73 }
 74 void bfs1(int node,int root){
 75     memset(q,0,sizeof(q));
 76     int front=1,tail=1; q[front]=node;
 77     while (front<=tail){
 78         int x=q[front]; front++; vis[x]=true;
 79         f[root]=max(f[root],dis[x]-dis[root]);
 80         for (int i=first[x];i!=-1;i=next[i])
 81         if (e[i].to!=nextn[node] && !vis[e[i].to]){
 82             tail++; q[tail]=e[i].to;
 83         }
 84     }
 85 }
 86 int main(){
 87     read(n); read(s);
 88     FORP(i,1,n) first[i]=-1;
 89     FORP(i,1,n-1) {
 90         int x,y,l;
 91         read(x); read(y); read(l);
 92         addedge(x,y,l); addedge(y,x,l);
 93     }
 94     maxx=0; bfs(1,0);
 95     memset(vis,false,sizeof(vis));
 96     maxx=0; bfs(temp[0],1);
 97     int L=temp[1],R=temp[0];
 98     memset(vis,false,sizeof(vis));
 99     for (int i=L;i!=R;i=pre[i]) nextn[pre[i]]=i;
100     L=temp[0],R=temp[1];
101     for (int i=L;i!=R;i=nextn[i]) { vis[i]=true; bfs1(i,i); }
102     vis[R]=true; bfs1(R,R);
103     int llen=0,rlen=maxx,head=L,tail=L,len=0;
104
105     while (true){
106         if (head==R) break;
107         if (dis[nextn[head]]<s) {
108             len=dis[nextn[head]];
109             head=nextn[head];
110             b.insert(f[head]);
111             rlen=maxx-dis[head];
112             continue;
113         }
114         break;
115     }
116
117     int t=max(rlen,max(llen,*b.begin()));
118     if (t<ans) ans=t;
119     while (head!=R){
120         len=dis[nextn[head]];
121         head=nextn[head];
122         rlen=maxx-dis[head];
123         while(len-dis[tail]>s){
124             b.erase(f[tail]); tail=nextn[tail]; llen=dis[tail];
125         }
126         t=max(rlen,max(llen,*b.begin()));
127         if (t<ans) ans=t;
128     }
129     printf("%d",ans);
130 }

时间: 2024-10-27 06:02:59

BZOJ 2282 & 树的直径的相关文章

BZOJ 2657 ZJOI 2012 旅游(journey) 树的直径

题目大意:给出一个凸多边形的三角剖分图,每一个三角形代表一个城市,现在连接这个图中的两个点,问最多能够经过多少个城市. 思路:浙江都是一帮神么.. 这题给的条件简直是不知所云啊..转化十分巧妙.因为每个凸n边形经过三角剖分之后会出现n - 2个三角形,任意一条边只会成为两个城市的公共边或者整个多边形的边.不难推出两个城市的公共边是n - 3条,也就是说把公共边看成是新图的边的话,就会新图就会构成一颗树.之后就是很水的树的直径了... CODE: #include <map> #include

BZOJ 3363 POJ 1985 Cow Marathon 树的直径

题目大意:给出一棵树.求两点间的最长距离. 思路:裸地树的直径.两次BFS,第一次随便找一个点宽搜.然后用上次宽搜时最远的点在宽搜.得到的最长距离就是树的直径. CODE: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 80010 using namespace std; int

[bzoj1791][ioi2008]Island 岛屿(基环树、树的直径)

bzoj luogu 题意可能会很绕 一句话:基环树的直径. 求直径: 对于环上每一个点记录其向它的子树最长路径为$dp_x$ 之后记录环上边长前缀和$ns_i$ dp值为$max_{i,j}dp[i]+sum[i]+dp[j]-sum[j]$ $dp[j]-sum[j]$提出来进单调队列. O(n). 记得dfs改bfs. #include<cstdio> #include<algorithm> using namespace std; typedef long long lin

poj 1985 Cow Marathon 【树的直径】

题目:poj 1985 Cow Marathon 题意:给出一个树,让你求树的直径. 分析: 树的直径:树上两点之间的最大距离. 我们从任意一点出发,BFS一个最远距离,然后从这个点出发,在BFS一个最远距离,就是树的直径. AC代码: /* POJ:1985 Cow Marathon 2014/10/12/21:18 Yougth*/ #include <cstdio> #include <iostream> #include <algorithm> #include

poj1849(求树的直径)

题目链接:http://poj.org/problem?id=1849 题意:有一颗n个结点的带权的无向树, 在s结点放两个机器人, 这两个机器人会把树的每条边都走一遍, 但是最后机器人不要求回到出发点. 问你两个机器人走的路总长之和的最小值是多少? 分析:如果从某点出发遍历完一棵树再回来,那么所有边都会走两遍,而从某点有两个机器人出发去遍历,因为不用回来,所以最后那两个人距离越远越好,可以从树的直径上某个点背道而驰,那么这段距离(树的直径)只走了一遍,其他的要走两遍,所以ans=sum*2-l

SDUT OJ 3045 迷之图论 (树的直径)

题目地址:SDUT OJ 3045 这题比赛的时候想的差不多..但是总是觉得不对..写了一次就没再写,然后删了..当时没想到的是第二次求出来的就是最长链..当时想到的两次bfs找最大值(这一种方法其实结果也对..TAT..),还有找到点后在回溯减去重点等等..但总觉得好像都不太对...赛后才知道这题原来是树的直径.....牡丹江区域现场赛的时候遇到过,不过赛后也没看... 找树的直径的方法其实就是先任取一点进行bfs,找到最远的一点,这时最远的一点肯定是最长链端点之一,然后再从这一最远点开始bf

hdu4612 无向图中任意添加一条边后使桥的数量最少 / 无向图缩点+求树的直径

题意如上,含有重边(重边的话,俩个点就可以构成了边双连通). 先缩点成树,在求数的直径,最远的连起来,剩下边(桥)的自然最少.这里学习了树的直径求法:第一次选任意起点U,进行bfs,到达最远的一个点v(level最深)该点必然是树的直径的一个端点,,再从该点出发,bfs,到最深的一点,该点深度就是直径.(证明:先假设u,是直径上一点,S,T是直径的端点,设v!=t,则有(V,U)+(U,S)>(T,U)+(U,S),矛盾,故t=v:若u不是直径上一点,设u到直径上的一点为x,同理易证. 最后 缩

ZOJ Problem Set - 3820 Building Fire Stations 【树的直径 + 操作 】

题目:problemId=5374" target="_blank">ZOJ Problem Set - 3820 Building Fire Stations 题意:给出n个点,n-1条边的一棵树.然后要在两个点上建立两个消防站.让全部点的到消防站最大距离的点的这个距离最小. 分析:首先先求这个树的直径.然后在树的直径的中点处把树分成两棵树.然后在把两棵树分别取中点的最大值就是ans值. 这个题目数据有点水了感觉... AC代码: #include <cstdi

BDFZOI 树的直径

提交次数:2 涉及知识:基础图论/BFS 描述 一棵树T的"直径"定义为结点两两间距离的最大值.给定带权树T,求T的直径长度. 输入 第一行包含2个整数N.M,表示图中共有N个结点和M条无向边.(N <= 5000,M<n)接下来M行,每行包含3个整数{u,v,w},表示有一条无向边连接结点u.v*输入保证是无环图输出一个整数,代表直径长度 样例输入 4 31 2 12 3 22 4 3 样例输出 5 代码: 1 #include<iostream> 2 #in