洛谷 P10678 题解

P10678 『STA - R6』月

题目描述

对于一棵有 n n n 个节点的树 T T T,定义其直径 diam ⁡ ( T ) \operatorname{diam}(T) diam(T) 为任意两个节点之间距离的最大值。

给定正整数 n n n 和每个点 i i i 的度数 d i d_i di,你需要构造一棵树 T ′ T^\prime T,同时最小化 diam ⁡ ( T ′ ) \operatorname{diam}(T^\prime) diam(T)

保证至少存在一棵符合要求的树,若存在多个符合要求的答案,输出任意一个即可。

样例 #1

样例输入 #1

4
2
1 1
3
1 1 2
5
1 1 2 2 2
7
1 3 2 3 1 1 1

样例输出 #1

2 1
1 3
3 2
5 4
4 2
3 1
3 5
4 2
3 2
1 2
5 4
6 4
7 3

Subtask骗分

我们先来看看部分分:

Subtask 编号数据范围分值
1 n ≤ 5 n \le 5 n5 17 17 17
2 d i ≤ 2 d_i \le 2 di2 23 23 23
3 d d d 中只含有两种本质不同的元素 26 26 26
4无特殊限制 34 34 34

先来分类讨论:

对于Subtask 1,这个 n ≤ 5 n \le 5 n5 我真觉得没必要说,dfs应该都能过,但是我没有试

对于Subtask 1, d i ≤ 2 d_i \le 2 di2,这就意味着这棵树只能是一个链,而且可以证明,有且只有 2 个节点的 d i d_i di 可以为 1,否则这棵树将无法构建出 n − 1 n - 1 n1 条边。

我比赛的时候看到这个就异常的激动,就立刻敲代码,然后WA了一个小时。。。

23分代码:

#include<bits/stdc++.h>
#define rep(i,s1,s2,s3) for(i = s1;i <= s2;i += s3)
#define r(i,s1,s2,s3) for(i = s1;i >= s2;i -= s3)
#define sort stable_sort
#define ull unsigned ll
#define INF 0x7f7f7f7f
#define ll long long
using namespace std;
int a[200010],d[200010];
bool f[200010];
int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#endif
    int i,n,t;
    cin>>t;
    while(t--){
        cin>>n;
        memset(f,0,sizeof(f));
        a[0] = 0;
        rep(i,1,n,1) cin>>d[i];
        rep(i,1,n,1) if(d[i] == 1) a[++a[0]] = i,f[i] = 1;
        cout<<a[1]<<' ';
        rep(i,1,n,1) if(!f[i]) cout<<i<<'\n'<<i<<' ';
        cout<<a[2]<<'\n';
    }
    return 0;
}

对于Subtask 3, d d d 中只含有两种本质不同的元素,可以证明, d d d 中必有一个及以上的 1,否则这棵树就会成为一个环基环树,而且这棵树必须为满 n n n 叉树 ( n ≥ 2 ) (n \ge 2) (n2),思路也非常清晰了,我也懒得写代码了

正解

题目中树的直径(即 diam ⁡ ( T ) \operatorname{diam}(T) diam(T))不会的可以去看看oi-wiki,讲得比较好,至少比某些章节讲得好一点

树的直径具有以下几个性质:

我们先设树的最大深度为 d m a x d_{max} dmax,树的直径为 l l l

那么

d m a x ≥ l 2 l ≤ d m a x × 2 d_{max} \ge \frac{l}{2} \\ l \le d_{max} \times 2 dmax2lldmax×2

所以,我们可以最小化 d m a x d_{max} dmax 保证 l l l 最小。如果这棵树 l ≤ ( d m a x − 1 ) × 2 l \le (d_{max} - 1) \times 2 l(dmax1)×2,那么我们一定能构造出 d m a x d_{max} dmax 为当前 d m a x − 1 d_{max} - 1 dmax1 的树,所以 d m a x × 2 − 1 ≤ l ≤ d m a x × 2 d_{max} \times 2 - 1 \le l \le d_{max} \times 2 dmax×21ldmax×2。如果我们想要使 l = d m a x × 2 − 1 l = d_{max} \times 2 - 1 l=dmax×21,那么根节点的所有子树中有且只有 1 个子树的最大深度为 d m a x − 1 d_{max} - 1 dmax1,其余子树的最大深度为 d m a x − 2 d_{max} - 2 dmax2,所以我们尽量使得子树的叶节点数量最大化。

要求最小化最大深度,可以发现,若 u , v u,v u,v 满足 u u u 的深度比 v v v 大且 d u ≥ d v d_u \ge d_v dudv,那么 u , v u,v u,v 交换一定可以使得该树在深度不变的情况下容纳更多的节点。

所以,我们只需对 d d d 从大到小排序,让深度更小的点的 d d d 更大,这样构造一定不劣。

时间复杂度: O ( ∑ n l o g ( n ) ) O(\sum_{}^{}nlog(n)) O(nlog(n))

#include<bits/stdc++.h>
#define rep(i,s1,s2,s3) for(i = s1;i <= s2;i += s3)
#define r(i,s1,s2,s3) for(i = s1;i >= s2;i -= s3)
#define ull unsigned long long
#define sort stable_sort
#define INF 0x7f7f7f7f
#define int long long
using namespace std;
int n;
struct node{
    int deep,id;
}a[200010];
bool cmp(node s1,node s2){
    return s1.dep < s2.dep;
}
int main(){
    ios::sycn_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONIINE_JUDGE
#else
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#endif
    int i,t,id;
    cin>>t;
    while(t--){
        cin>>n;
        rep(i,1,n,1) cin>>a[i].dep,a[i].id = i;
        sort(a + 1,a + n + 1,cmp);
        id = 1;
        rep(i,2,n,1){
            while(!a[id].dep && id < i - 1) id+;
            a[id].dep--;
            a[i].dep--;
            cout<<a[i].id<<' '<<a[id].id<<'\n";
        }
    }
    return 0;
}
  • 11
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值