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

思路:

牡丹江站的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. PAT 1042 字符统计(20)(思路)

    1042 字符统计(20)(20 分) 请编写程序,找出一段给定文字中出现最频繁的那个英文字母. 输入格式: 输入在一行中给出一个长度不超过1000的字符串.字符串由ASCII码表中任意可见字符及空格 ...

  2. Java 内存模型、GC原理及算法

    Java 内存模型.GC原理:https://blog.csdn.net/ithomer/article/details/6252552 GC算法:https://www.cnblogs.com/sm ...

  3. python——简单爬虫

    因为要学习python,所以看到一些网站有很多文章. 如:http://python.jobbole.com/all-posts/ 目标: 将某个网站脚本编程->python模块这个分类下所有的 ...

  4. 论坛:一对一关联映射/单向关联/两个类间,可以有两个(多个)关联关系/content为大文本类型/

    >>单向:只写一端的映射属性,另一端不写(有一端用不着);双向:两端都写映射属性 >>一对一关联有两类:一类基于主键的(一般不使用),一类基于外键的(重点学习): 外键:是一个 ...

  5. 【UI测试】--美观与协调性

  6. EASYUI DATAGRID 改变行值

    在easyui datagrid 中如果要 改变当前选中行的值又不想用编辑状态,或者想从外部改变某一行的值,下面的方法可以做到 function test() {             var ro ...

  7. http://itellyou.cn/

    http://itellyou.cn/ 这里提供了微软MSDN上所有能下载的软件. 下载完记得检验. 这是更详细的介绍:http://wenku.baidu.com/link?url=_dZ0mYvl ...

  8. 【Web】Nginx配置规则

    Nginx配置基本说明 以下是nginx的基本配置文件如下(编辑命令:vi /usr/local/nginx/conf/nginx.conf): #user nobody; #nginx进程数,建议设 ...

  9. kbmmw 中的进程管理小工具

    kbmmw 5.6.20 发布了,本版本带来一个小功能,就是可以在kbmmw 应用里面建立和管理进程, 虽然你可以直接调用windows api 做类似的事情,但是kbmmw 里面简化了操作,也加强了 ...

  10. 初识kbmmw 中的ORM

    在kbmmw 5.02.1 中,加入了ORM 的功能(这里可能和其他语言的定义不完全一样),我们就简单的认为 它就是一个类与数据库的转换吧.今天就先介绍一下如何通过kbmmw 的ORM 功能,实现类与 ...