Educational Codeforces Round 105 (Rated for Div. 2) D. Dogeforces

类别:分治

题目描述简化变形:

 给你n个叶子节点,以及两两叶子节点之间的最近公共祖先的权值,让你构造一棵树,要求子节点权值严格小于当前节点权值。

思路:

  如果题目变形成上述,不知道你的思路是否开阔了?
  我们可以发现,树根的权值一定是最大的。叶子节点的数目只有500,因此可以暴力N^2枚举,找两个叶子节点X、Y他们的最近公共祖先的权值为当前树的最大权值节点,根据题目的隐含要求,最大权值节点一定是当前树的根节点。接着随便给这个根节点root一个编号,以X节点为路径一端,枚举另一端叶子节点Z,如果两者的最近公共祖先的权值与之前所求最大权直接点相等,说明X-Z的路径过当前树的根节点,这也说明Z节点不在X节点一侧。以是否在X节点一侧为条件进行划分,整颗树变成两棵树,接着继续上述操作,进行分治操作。
在这里插入图片描述红色为节点的权值,黑色为节点编号,假设现在以节点1为端点进行枚举,可以发现如果最近公共祖先的权值为10都在绿色一侧,其他都为蓝色一侧。因此可以进行分治

#include <bits/stdc++.h>
#define pb push_back
#define ALL(x) x.begin(),x.end()
#define fio ios::sync_with_stdio(false);cin.tie(0);
const double pi = acos(-1);
const int N = 510;
using namespace std;
int n;
int a[N][N];
int num;
int fa[N*2];
int salary[N*2];
int work(vector<int>vec, int F = -1){
    if(vec.size() == 1){
        if(F != -1) fa[vec[0]] = F;
        return vec[0];
    }
    int MAX = -1, z = -1;
    if(F != -1) {
        MAX = salary[F], z = vec[0];
        vector<int>V[2];
        V[0].pb(z);
        for(int i = 1; i < vec.size(); ++i){
            int u = vec[i];
            if(MAX != a[z][u])V[0].pb(u);
            else V[1].pb(u);
        }
        fa[work(V[0])] = F;
        if(V[1].size())work(V[1], F);
        return F;
    }
    for(int i = 0; i < vec.size(); ++i){
        for(int j = i+1; j < vec.size(); ++j){
            int u = vec[i], v = vec[j];
            if(MAX < a[u][v]) MAX = a[u][v], z = u;
        }
    }
    int now = ++num; //给当前树的根节点一个编号
    salary[now] = MAX;//当前树根节点的权值,即为两两枚举的最大值
    vector<int>V[2];
    V[0].pb(z);//V[0]存放X一侧子树,V[1]存放另一侧
    for(int i = 0; i < vec.size(); ++i){
        if(z == vec[i]) continue;
        int u = vec[i];
        if(MAX != a[z][u])V[0].pb(u);
        else V[1].pb(u);
    }
    fa[work(V[0])] = now;
    if(V[1].size())work(V[1], now);
    return now;
}
int main(){
    fio;
    cin >> n;
    num = n;
    vector<int>vec;
    for(int i = 1; i <= n; ++i){
        vec.pb(i);
        for(int j = 1; j <= n; ++j){
            cin >> a[i][j];
            if(i == j)salary[i] = a[i][j];
        }
    }
    int head = work(vec);
    cout<<num<<endl;
    for(int i = 1; i <= num; ++i)cout<<salary[i]<<" ";cout<<endl;
    cout<<head<<endl;
    for(int i = 1; i <= num; ++i){
        if(i != head) cout<<i<<" "<<fa[i]<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值