Distance Tree (easy version) (单调性+划分+暴力枚举)

Distance Tree (easy version)

[Link](Problem - E1 - Codeforces)

题意

给你一棵树,边权为 1 1 1 d ( u ) : u 结 点 与 1 号 点 之 间 的 距 离 d(u):u结点与1号点之间的距离 d(u):u1,设 f ( x ) : 所 有 d ( i ) 中 的 最 大 值 f(x):所有d(i)中的最大值 f(x)d(i)

你有 n n n次操作(相互独立)第 i i i次可以添加一条边全为 i i i的边,问你添加完这条边后 f ( x ) f(x) f(x)最小是多少。

数据范围: 2 ≤ n ≤ 3000 2\le n\le 3000 2n3000

思路

​ 首先加的这条边一定是 ( 1 → u ) (1\to u) (1u)这样的,因为如果这样不是最优的,设最优解为 ( u → v ) (u\to v) (uv),那么减少的一定是 v v v的子树的距离,那么我们直接 ( 1 → v ) (1\to v) (1v)连上一条边,只会让距离更小。

​ 我们可以暴力的枚举这条边连向哪个结点,假设为 u u u,那么连接以后的距离 d ( v ) d(v) d(v)会有两种情况:
在这里插入图片描述

  1. 原来从一号点直接连过来的长度设 a v a_v av

  2. 先从一号点经过加的边连到 u u u再从 u u u v v v, 设 u u u v v v的距离为 b v b_v bv则当前长度为 b v + x b_v+x bv+x

如果这样暴力的枚举每一个点,然后再枚举新边长度 x x x,再枚举所有的点判断当前局面的 f ( x ) f(x) f(x) O ( n 3 ) O(n^3) O(n3)

​ 考虑性质发现对于 a v a_v av b v + x b_v+x bv+x,当 a v > b v + x a_v>b_v+x av>bv+x的时候这个 d ( v ) d(v) d(v)才会变小,那么对于某个 x x x而言所有的点就会分成两部分,一个是 a v < b v + x a_v<b_v+x av<bv+x的点,这些点还是原来的距离,一个是 a v > b v + x a_v>b_v+x av>bv+x的点,这些点的距离里的最大值就是我们第二部分的解。

​ 随着 x x x增大一定是一些通过第二种走法会变小的点不能变小了,因此发现具有单调性,所以我们可以按照 a v − b v a_v-b_v avbv给所有的点排序,然后枚举 x x x,找到当前 x x x对应的断点,即起前面是第一部分的解,后面是第二部分的解,对于第二部分的解,我们可以先预处理出排序以后的 b i b_i bi的后缀最大值就可以 O ( 1 ) O(1) O(1)的查找第二部分的解了,对于每个 x x x判断一下当下的最优解就是一二部分大的那个。

复杂度 O ( n 2 ) O(n^2) O(n2)

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
PII a[N];
int dist[N];
int dep[N];
int mx[N], res[N];
bool cmp(PII a, PII b) {
    return (a.x - a.y) < (b.x - b.y);
}
void dfs(int u, int fa, int d) {
    dist[u] = d;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa) continue ;
        dfs(j, u, d + 1);
    }
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        memset(h, -1, sizeof h);        
        
        for (int i = 1; i < n; i ++) {
            int a, b; cin >> a >> b;
            add(a, b), add(b, a);
        }

        dfs(1, 0, 0);
        int mmx = 0;
        for (int i = 1; i <= n; i ++) dep[i] = dist[i], mmx = max(mmx, dep[i]);
        for (int i = 1; i <= n; i ++) res[i] = mmx;
        
        for (int i = 2; i <= n; i ++) {
            dfs(i, 0, 0);

            for (int j = 1; j <= n; j ++) a[j].x = dep[j], a[j].y = dist[j];
            sort(a + 1, a + 1 + n, cmp);

            mx[n + 1] = 0;
            int p = 1, tmp = 0;
            for (int j = n; j; j --)  mx[j] = max(mx[j + 1], a[j].y);
        
            for (int k = 1; k <= n; k ++) {
                while (p <= n && a[p].x - a[p].y <= k) tmp = max(tmp, a[p].x), p ++;
                res[k] = min(res[k], max(k + mx[p], tmp));
            }
        }

        for (int i = 1; i <= n; i ++) cout << res[i] << ' ';
        cout << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值