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 n≤5 | 17 17 17 |
2 | d i ≤ 2 d_i \le 2 di≤2 | 23 23 23 |
3 | d d d 中只含有两种本质不同的元素 | 26 26 26 |
4 | 无特殊限制 | 34 34 34 |
先来分类讨论:
对于Subtask 1,这个
n
≤
5
n \le 5
n≤5 我真觉得没必要说,dfs应该都能过,但是我没有试
对于Subtask 1, d i ≤ 2 d_i \le 2 di≤2,这就意味着这棵树只能是一个链,而且可以证明,有且只有 2 个节点的 d i d_i di 可以为 1,否则这棵树将无法构建出 n − 1 n - 1 n−1 条边。
我比赛的时候看到这个就异常的激动,就立刻敲代码,然后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)
(n≥2),思路也非常清晰了,我也懒得写代码了
正解
题目中树的直径(即 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 dmax≥2ll≤dmax×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≤(dmax−1)×2,那么我们一定能构造出 d m a x d_{max} dmax 为当前 d m a x − 1 d_{max} - 1 dmax−1 的树,所以 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×2−1≤l≤dmax×2。如果我们想要使 l = d m a x × 2 − 1 l = d_{max} \times 2 - 1 l=dmax×2−1,那么根节点的所有子树中有且只有 1 个子树的最大深度为 d m a x − 1 d_{max} - 1 dmax−1,其余子树的最大深度为 d m a x − 2 d_{max} - 2 dmax−2,所以我们尽量使得子树的叶节点数量最大化。
要求最小化最大深度,可以发现,若 u , v u,v u,v 满足 u u u 的深度比 v v v 大且 d u ≥ d v d_u \ge d_v du≥dv,那么 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;
}