题目描述
因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n − 1 条边的无向连通图),由于新建的办公室太大以至于要将奖杯要分放在两个不同的地方以便同学们丢硬币进去开光,OB 想请你帮帮他看看奖杯放在哪两个办公室使得在任意一个在劳模办公室做题的小朋友能最快地找到奖杯来开光。
一句话题意:给出一个 n 个点的树,在两个合适且不同的点放上奖杯,使得每个点到最近的奖杯距离最大值最小。
输入
第一行,一个整数 n。
接下来的 n − 1 行,每行两个数 x y
输出
一个数,表示最小的最大距离。
样例输入
8
1 2
1 3
2 4
2 5
3 6
3 7
1 8
样例输出
2
提示
对于前 60% 的数据,n ≤ 100。
对于前 80% 的数据,n ≤ 2000。
对于 80% 的数据,保证树的形态随机。
对于 100% 的数据,保证 3 ≤ n ≤ 200000。
这道题有好多种解法,把题解贴一贴~
3.1 60% O(n3)
n2 枚举两个奖杯位置,再 O(n) 扫一遍看看每个位置离最近奖杯最远是多少。
3.2 80% O(n2)
考虑两个奖杯管辖的区域必定有一个边界,我们枚举这个边界,也就是一条边,其中一部
分是子树,一部分是子树外,我们只要分别求出另外两个树的直径。
3.3 树形态随机
期望树的直径很短,两个奖杯都在直径上枚举。
3.4 100% 二分答案 1 O(nlogn)
奖杯在直径上,二分答案后取离直径上离端点距离答案的点,遍历 check 一遍。
3.5 100% 二分答案 2 O(nlogn)
随便提一个节点为根,二分答案,深度最深的节点一定要被照顾到,所以最深的点往上跳
答案层即可,和其距离答案以内的点都删掉,再做一次。(我最开始的做法QAQ)
此法可以拓展到 k 个奖杯,由皮皮轩友情提供。
3.6 100% 树形 dp O(n)
在 80 分的基础上用树形 dp,记下每个点向下前三长和向上一格后不回该子树最长的路径
长度。子树内直径是前两长的和与该子树各个子树直径取 max;子树外直径是父节点向上一格
后不回该子树最长的路径长度,前两长不进入该子树的向下最长路径这三条取前两长加起来与
父节点以上的答案取 max。
我使用的应该算第二种二分。
也就是先二分答案——最短距离x,再按深度从大到小枚举节点,如果发现有一个节点距最近奖杯的距离大于x,即!v[i],那么找它的父亲的父亲的父亲。。。的父亲(共x个父亲)t,在t节点放置一个奖杯,然后搜索将和它距离小于等于x的节点,标记v[i]等于1。
如果发现v[i] == 0 && cnt >= 2 就直接返回0,因为已经没有更多的奖杯可以放了。这种贪心策略很容易证明,这里不再赘述。
我的AC之路多么坎坷——
1.用了DFS,自己生成一个200000个节点的链形树,结果发现RE,怀疑可能爆栈,我改。x
2.用了BFS,然后随机生成一棵200000个节点的树,虽然过了,但是发现时间有点慢,用了clock()输出所用时间,发现用了1000+ms,可能会TLE,我再改。
3.输出 读入所用时间,发现已经是几百ms,毅然将scanf改为读优,用了一些register,降了一些复杂度,但仍然不能满足要求,我还要改。
4.怀疑BFS时直接用了queue,复杂度会倍增,于是手打队列,发现复杂度一下子减掉了一半,只有400~500+ms水平,估计没有问题,但是仍然打了对拍(其实DFS打好以后就打了对拍),没有发现错误,实际上也AC了。
给出了那些我打的代码QAQ
#include<bits/stdc++.h> using namespace std; #define MAXN 200005 int n, x, y; int head[MAXN], nxt[MAXN << 1], to[MAXN << 1], tot; int dis[MAXN]; inline void Add( int x, int y ){ to[++tot] = y; nxt[tot] = head[x]; head[x] = tot; } void dfs( int x, int fa, int d ){ dis[x] = min( dis[x], d ); for ( int i = head[x]; i; i = nxt[i] ) if ( to[i] != fa ) dfs( to[i], x, d + 1 ); } int main(){ scanf( "%d", &n ); for ( int i = 1; i < n; ++i ){ scanf( "%d%d", &x, &y ); Add( x, y ); Add( y, x ); } int ans(0x7f7f7f7f); for ( int i = 1; i <= n; ++i ) for ( int j = i + 1; j <= n; ++j ){ memset( dis, 0x3f, sizeof dis ); dfs( i, i, 0 ); dfs( j, j, 0 ); int cur(0); for ( int k = 1; k <= n; ++k ) cur = max( cur, dis[k] ); ans = min( ans, cur ); } printf( "%d\n", ans ); return 0; }
#include<bits/stdc++.h> using namespace std; #define MAXN 200005 int n, x, y; int head[MAXN], nxt[MAXN << 1], to[MAXN << 1], tot; int dep[MAXN], o[MAXN], f[MAXN]; int v[MAXN]; bool cmp( int x, int y ){ return dep[x] > dep[y]; } inline void Add( int x, int y ){ to[++tot] = y; nxt[tot] = head[x]; head[x] = tot; } void DFS( int x, int fa, int d ){ dep[x] = d; f[x] = fa; for ( int i = head[x]; i; i = nxt[i] ) if ( to[i] != fa ) DFS( to[i], x, d + 1 ); } void dfs( int x, int fa, int d, int maxd ){ if ( d > maxd ) return; v[x] = 1; for ( int i = head[x]; i; i = nxt[i] ) if ( to[i] != fa ) dfs( to[i], x, d + 1, maxd ); } bool check( int x ){ int cur(0); memset( v, 0, sizeof v ); for ( int i = 1; i <= n; ++i ){ if ( !v[o[i]] ){ int t(o[i]); for ( int j = 1; j <= x; ++j ) t = f[t]; cur++; if ( cur > 2 ) return 0; dfs( t, t, 0, x ); } } return 1; } int main(){ scanf( "%d", &n ); for ( int i = 1; i < n; ++i ){ scanf( "%d%d", &x, &y ); Add( x, y ); Add( y, x ); } DFS( 1, 1, 1 ); for ( int i = 1; i <= n; ++i ) o[i] = i; sort( o + 1, o + n + 1, cmp ); int l(1), r(200000), mid, ans(-1); while( l <= r ){ mid = ( l + r ) >> 1; if ( check( mid ) ) r = mid - 1, ans = mid; else l = mid + 1; } printf( "%d\n", ans ); return 0; }
#include<bits/stdc++.h> using namespace std; #define MAXN 400005 int n, x, y; int head[MAXN], nxt[MAXN << 1], to[MAXN << 1], tot; int dep[MAXN], o[MAXN], f[MAXN]; int v[MAXN]; bool cmp( int x, int y ){ return dep[x] > dep[y]; } inline void Add( int x, int y ){ to[++tot] = y; nxt[tot] = head[x]; head[x] = tot; } void BFS(){ queue<int> Q; Q.push(1); f[1] = 1; dep[1] = 1; while( !Q.empty() ){ int t(Q.front()); Q.pop(); for ( int i = head[t]; i; i = nxt[i] ) if ( to[i] != f[t] ){ dep[to[i]] = dep[t] + 1; f[to[i]] = t; Q.push(to[i]); } } } struct states{ int id, de, fa; }; states make_st( int x, int y, int z ){ states t; t.id = x; t.de = y; t.fa = z; return t; } void bfs( int x, int maxd ){ queue<states> Q; Q.push( make_st( x, 0, x ) ); while( !Q.empty() ){ int t(Q.front().id), d(Q.front().de), fth(Q.front().fa); Q.pop(); v[t] = 1; if ( d >= maxd ) continue; for ( int i = head[t]; i; i = nxt[i] ) if ( to[i] != fth ) Q.push( make_st( to[i], d + 1, t ) ); } } bool check( int x ){ int cur(0); memset( v, 0, sizeof v ); for ( int i = 1; i <= n; ++i ){ if ( !v[o[i]] ){ int t(o[i]); for ( int j = 1; j <= x; ++j ) t = f[t]; cur++; if ( cur > 2 ) return 0; bfs( t, x ); } } return 1; } int main(){ scanf( "%d", &n ); for ( int i = 1; i < n; ++i ){ scanf( "%d%d", &x, &y ); Add( x, y ); Add( y, x ); } BFS(); for ( int i = 1; i <= n; ++i ) o[i] = i; sort( o + 1, o + n + 1, cmp ); int l(1), r(200000), mid, ans(-1); while( l <= r ){ mid = ( l + r ) >> 1; if ( check( mid ) ) r = mid - 1, ans = mid; else l = mid + 1; } printf( "%d\n", ans ); return 0; }
树形DP我还没打过,只能等以后再来填坑了。
祝大家AC愉快QAQ。