题意:给定一个树,找出两个点,使得其他点到最近的点的距离最小

思路:

牡丹江站的B题。。可惜当时坑的不大对,最后也没写完。。

1、题解方法:

基于一个结论,答案一定在直径上(证明我不会)。。

那么,可以先求出直径,然后直接二分,二分完后o(n)判定,时间复杂度为nlogn

2、我的方法:

赛场上写的,可惜最后由于各种原因没写完,代码难度实在比题解高多了,可惜想到了就不敢在猜想其他方法了了。。

可以很容易证明,题目等价于对于删除某条边后求剩余两棵树直径,然后取一个最小的。。

那么,就可以用树形dp的方法,维护每个点为子树的前三长链(以根为起始,并且来源于不同子树),还有不经过根的前两大答案,以及以其为根的子树的答案。

先一边求完后,然后从根递推到以每个点为根成为一个树,其余为另外一棵树的答案啊。。

这样时间复杂度为o(n)

code(nlog(n)):

 #include <bits/stdc++.h>
#define M0(a, b) memset(a, 0, sizeof(int) * (b+10))
using namespace std;
const int maxn = ;
int z[maxn], inz[maxn], pos[maxn], from[maxn];
int L[maxn], R[maxn], cov[maxn];
vector<int> e[];
int n, m;
int ans, ansx, ansy; void init(){
scanf("%d", &n);
for (int i = ; i <= n; ++i)
e[i].clear();
int u, v;
for (int i = ; i < n; ++i){
scanf("%d%d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
}
} int pre[maxn], inq[maxn], dis[maxn];
void bfs(int s, int &rt){
queue<int> q;
M0(inq, n), M0(dis, n), M0(pre, n);
q.push(s), dis[s] = , inq[s] = ;
int u, v;
while (!q.empty()){
u = q.front();
q.pop();
for (int i = ; i < (int)e[u].size(); ++i){
v = e[u][i];
if (!inq[v])
dis[v] = dis[u] + , inq[v] = , q.push(v), pre[v] = u;
}
}
rt = s;
for (int i = ; i <= n; ++i)
if (dis[rt] < dis[i]) rt = i;
} void bfs(){
queue<int> q;
M0(inq, n), M0(dis, n), M0(from, n);
for (int i = ; i <= m; ++i)
q.push(z[i]), inq[z[i]] = , from[z[i]] = i, dis[z[i]] = ;
int u, v;
while (!q.empty()){
u = q.front();
q.pop();
for (int i = ; i < (int)e[u].size(); ++i){
v = e[u][i];
if (!inq[v])
dis[v] = dis[u] + , inq[v] = , q.push(v), from[v] = from[u];
}
}
} int check(const int len, int& x, int &y){
M0(L, n), M0(R, n), M0(cov, n);
int d;
int mleft = m + , mright = ;
for (int i = ; i <= n; ++i){
d = len - dis[i];
if (d < ) return ;
L[i] = max(, from[i] - d);
R[i] = min(m, from[i] + d);
mleft = min(mleft, R[i]);
mright = max(mright, L[i]);
}
for (int i = ; i <= n; ++i)
if ((L[i] <= mleft && R[i] >= mleft) || (L[i] <= mright && R[i] >= mright)) continue;
else return ;
x = mleft, y = mright;
if (x == y)
(y < m) ? ++y : --x;
return ;
} void solve(){
int s, t;
bfs(, s), bfs(s, t);
m = ;
M0(inz, n), M0(pos, n);
while (t) z[++m] = t, pos[t] = m, t = pre[t];
bfs();
int l = , r = n, mid;
int x, y;
while (l <= r){
mid = (l + r) >> ;
if (check(mid, x, y))
r = mid - , ans = mid, ansx = z[x], ansy = z[y];
else l = mid + ;
}
printf("%d %d %d\n", ans, ansx, ansy);
} int main(){
int cas;
scanf("%d", &cas);
while (cas--){
init();
solve();
}
}

code(o(n)):

 #include <bits/stdc++.h>
#define x first
#define y second
#define M0(a) memset(a, 0, sizeof(int) * (n+10))
#define Inf 0x3fffffff
using namespace std;
const int maxn = ;
vector<int> e[maxn];
pair<int, int> len[maxn][], ans2[maxn][], tmp[], one(, ), zero(, );
int fa[maxn], ans1[maxn], n, ans[maxn], z[maxn];
int ans_x, ans_y, ans_len; int inq[maxn], dis[maxn], pre[maxn];
int pos[maxn], tot;
void bfs(){
M0(inq), M0(fa);
queue<int> q;
q.push(), inq[] = , pos[tot = ] = ;
int u, v;
while (!q.empty()){
u = q.front();
q.pop();
for (int i = ; i < (int)e[u].size(); ++i){
v = e[u][i];
if (!inq[v])
fa[v] = u, q.push(v), inq[v] = , pos[++tot] = v;
}
}
} void gao1(const int& u){
int v;
for (int i = ; i < (int)e[u].size(); ++i){
v = e[u][i];
if (v == fa[u]) continue;
for (int j = ; j < ; ++j) tmp[j] = len[u][j];
tmp[] = make_pair(len[v][].x + , v);
sort(tmp, tmp + , greater<pair<int, int> >());
for (int j = ; j < ; ++j) len[u][j] = tmp[j];
if (ans1[v] > ans2[u][].x){
swap(ans2[u][], ans2[u][]);
ans2[u][] = make_pair(ans1[v], v);
} else if (ans1[v] > ans2[u][].x)
ans2[u][] = make_pair(ans1[v], v);
ans1[u] = max(ans1[u], ans1[v]);
}
ans1[u] = max(ans1[u], len[u][].x + len[u][].x - );
} int ff, s[];
int ss[maxn], max_d[maxn];
void gao2(const int &u){
ff = fa[u];
ss[u] = ss[ff];
if (len[ff][].y == u)
s[] = len[ff][].x, s[] = len[ff][].x;
else if (len[ff][].y == u)
s[] = len[ff][].x, s[] = len[ff][].x;
else
s[] = len[ff][].x, s[] = len[ff][].x;
s[] = max_d[ff];
sort(s, s + , greater<int>() );
ss[u] = max(s[] + s[] - , ss[u]);
max_d[u] = s[] + ;
if (ans2[ff][].y == u)
ss[u] = max(ss[u], ans2[ff][].x);
else
ss[u] = max(ss[u], ans2[ff][].x);
ans[u] = max(ss[u], ans1[u]);
} void pre_do(){
M0(ss), M0(max_d);
for (int i = ; i <= n; ++i){
len[i][] = len[i][] = len[i][] = one;
ans2[i][] = ans2[i][] = zero;
}
} void init(){
scanf("%d", &n);
pre_do();
for (int i = ; i <= n; ++i)
e[i].clear();
int u, v;
for (int i = ; i < n; ++i){
scanf("%d%d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
}
} void bfs(int s, int &t, const int& other){
queue<int> q;
M0(inq), M0(pre);
memset(dis, -, sizeof(int) * (n+));
q.push(s), dis[s] = , inq[s] = ;
int u, v;
while (!q.empty()){
u = q.front();
q.pop();
for (int i = ; i < (int)e[u].size(); ++i){
v = e[u][i];
if (v == other) continue;
if (!inq[v])
dis[v] = dis[u] + , inq[v] = , q.push(v), pre[v] = u;
}
}
t = s;
for (int i = ; i <= n; ++i)
if (dis[t] < dis[i]) t = i;
} void solve(){
M0(fa), M0(ans1);
bfs();
for (int i = n; i >= ; --i)
gao1(pos[i]);
for (int i = ; i <= n; ++i)
gao2(pos[i]);
int rt = ;
for (int i = ; i <= n; ++i)
if (ans[rt] > ans[i]) rt = i;
// cout << rt << endl;
ans_len = ans[rt] / ;
int x, y;
bfs(rt, x, fa[rt]);
bfs(x, y, fa[rt]);
int m = ;
while (y) z[m++] = y, y = pre[y];
ans_x = z[m/];
bfs(fa[rt], x, rt);
bfs(x, y, rt);
m = ;
while (y) z[m++] = y, y = pre[y];
ans_y = z[m/];
printf("%d %d %d\n",ans_len, ans_x, ans_y); } int main(){
int cas;
scanf("%d", &cas);
while (cas--){
init();
solve();
}
}

zoj3820的更多相关文章

  1. ZOJ-3820 Building Fire Stations 题解

    题目大意: 一棵树,在其中找两个点,使得其他点到这两个的距离的较小值的最大值的最小值及其方案. 思路: 首先显然一棵树的直径的中点到其他点的距离的最大值必定比其他点的小. 那么感性思考一下就将一棵树的 ...

  2. zoj3820 Building Fire Stations 树的中心

    题意:n个点的树,给出n-1条边,每条边长都是1,两个点建立防火站,使得其他点到防火站的最远距离最短. 思路:比赛的时候和队友一开始想是把这两个点拎起来,使得层数最少,有点像是树的中心,于是就猜测是将 ...

  3. 求树的直径和中心(ZOJ3820)

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5374 Building Fire Stations Time Limit: 5 ...

  4. zoj3820 树的直径+二分

    这题是个遗憾 !!!!!当时一直不敢相信两个站一定在直径上,赛后想想自己真的是脑袋抽风, 如果其中一个站不在直径上就反向的说明了这条不是直径.可以很明白我们可以肯定的是有一个点一定在直径上假如另外一个 ...

随机推荐

  1. hdu 1757 (矩阵快速幂) 一个简单的问题 一个简单的开始

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1757 题意不难理解,当x小于10的时候,数列f(x)=x,当x大于等于10的时候f(x) = a0 * ...

  2. 冒泡排序(js版)

    基本思想:两两比较相邻记录的关键字,如果反序则交换,直至没有反序为止. 最初的冒泡排序(初级版): //从小到大 function BubbleSort(arr){ var i,j,temp; for ...

  3. waf相关

    google上搜索下面信息: waf site:klionsec.github.io 有几篇比较有意思的问题: https://klionsec.github.io/2017/07/09/nginx- ...

  4. ATM作业

    关于ATM作业,最近做了很久,才明白,其实看了很久的作业视频讲解,到不如将作业的整个下载下来进行运行,去了解程序本身的结构和运行方式.首先说需求,就感觉是各种懵逼,这才学了函数,和模块之间的简单调用就 ...

  5. PHP--根据手机号-淘宝平台获取归属地运营商信息

    //获取手机账号信息 public function get_mobile_area($mobile){ $sms = array('province'=>'', 'supplier'=> ...

  6. Python module : simuPOP

    conda config --add channels conda-forge conda install simupop simuPOP is a general-purpose individua ...

  7. python之Flask框架

    一.简单的Flask框架 1)flask简介 Flask 是一个 web 框架.也就是说 Flask 为你提供工具,库和技术来允许你构建一个 web 应用程序. 这个 wdb 应用程序可以使一些 we ...

  8. Mac 更换桌面背景崩溃(闪退)

    更新完系统后就会出现这种情况,,其实就是用户偏好文件出了问题. 1. 在终端输入 cd /Users/YourUserName/Library/Preferences //进入文件夹 rm com.a ...

  9. MZOJ 1345 hero

    一道宽搜模版题,可写错了两个地方的我只得了56(掩面痛哭) http://10.37.2.111/problem.php?id=1345 先看看正确的 #include <bits/stdc++ ...

  10. 2018.12.19 atcoder Iroha and a Grid(组合数学)

    传送门 组合数学好题. 给你一个hhh行www列的网格,其中左下角aaa行bbb列不能走,问从左上角走到右下角有多少种走法(每次只能向右或者向下) 我们考虑分步计数. 我们一共能走的区域是总网格区域去 ...