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

思路:

牡丹江站的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. andorid 数据储存、SharedPreferences存储和手机内部储存

    .xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android ...

  2. iOS.CodeSign

    Inside Code Signing 1. Code Signing需要的基础组件: 证书,私钥 As an iOS developer, chances are you have a certif ...

  3. python之初接触

    编程语言相关 1什么是编程语言 编程语言即语言,语言的本质就是沟通,因而编程语言与英语 .法语.日语等所有语言并无区别,只不过英语是人与人之间沟通的介质,而编程语言则是程序员与计算机沟通的介质. 程序 ...

  4. BZOJ 1227 [SDOI2009]虔诚的墓主人 - 扫描线

    Solution 离散化 扫描线, 并用 $rest[i]$ 和 $cnt[i]$ 记录 第$i$列 总共有 $cnt[i]$棵常青树, 还有$rest[i]$ 没有被扫描到. 那么 第$i$ 列的方 ...

  5. linux安全分析

    history 查看历史命令 last | grep -i norco //最后一次登录时间

  6. Java 线程池 Executor 框架

    在Java中,可以通过new Thread 的方法来创建一个新的线程执行任务,但是线程的创建是非常耗时的,而且创建出来的新的线程都各自运行.缺乏统一的管理,这样的后果是可能导致创建过多的线程从而过度消 ...

  7. [Robot Framework] 如何在Setup中用Run Keywords执行多个带参数的关键字

    参考文档:http://www.howtobuildsoftware.com/index.php/how-do/bZ7q/robotframework-setup-teardown-robot-fra ...

  8. 论坛:排序 >>case..when..then ..end的妙用

    a.主题列表按 最后更新时间 进行排序 数据库SQL语句中没有if..else的判断语句,但是oracle中有decode()函数可以实现这种判断语句,但是还可以用case..when..then . ...

  9. linux_添加一个普通用户

    useradd 用户名 passwd 密码 su 用户名 可以切换用户 exit 返回之前登录的用户 sodu用户 --> 不用告诉普通用户root的密码 可以查看所有的系统文件 包括root下 ...

  10. 对SVC和SVR的理解

    首先: support vector classify(SVC)支持分类机做二分类的,找出分类面,解决分类问题 support vector regression(SCR)支持回归机做曲线拟合.函数回 ...