Codeforces Round #612 (Div. 2) D. Numbers on Tree(插入法 or 删除法)

题目链接:https://codeforces.com/contest/1287/problem/D

题目大意:给出每个点的父亲和该点的子树中含有值比该点值小的点个数,求每个点的值

题目思路:讲道理,又是直接自闭。。构造题。首先显而易见,如果想要孩子中小于当前值的数字等于x个,那首先至少需要有x个孩子,如果没有这么多个孩子直接白给,其他情况都是没问题。

有两种常见的做法:

删除法

  删除法的做法非常简单,就是直接dfs,每次给当前点赋的值就是还没有用到的数字中第c[u]个数字(从0开始数)
  这种做法的正确性在于,首先当前数字是第c[u]个,那么,只要前面c[u]-1个数字如果都是该点的孩子的值,那么答案成立。由于孩子的数量大于等于c[u],给孩子赋值又是按照顺序来,所以前面几个数字一定会赋给它的孩子。

 

插入法

  插入法的做法其实跟删除法异曲同工。不过插入法是自顶向下时直接赋值,先给父亲赋值,而插入法是先处理孩子。每次都把当前点插入到一个vector中,每次都插入到c[u]这个位置,由于一定是孩子插完才插当前点,所以vector中的元素一定大于等于c[u]。这个的正确性在于,每当你在插入一棵子树时,这棵子树的点就占据了前面的部分,那么在往里头插,每次孩子的数量又一定大于等于c[u],所以一定能插到自己子树的这部分内,也就是每次的插入一定都符合自己作为根的子树的合法位置,也就保证了答案的合法。

  其实说的头头是道,想的也非常有道理,但还是感觉哪里有点怪怪的,就是感觉以后再遇到相同类型的题还是很难想到,可能还是刷题量没够吧。

以下是代码:

删除法

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
const int MAXN = 2e3+5;
int n,x,c[MAXN],vis[MAXN],ans[MAXN],siz[MAXN],flag;
vector<int>v[MAXN];
void dfs(int u){
    if(flag)return;
    int pos=0;
    siz[u]=1;
    rep(i,1,n){
        if(vis[i]){
            if(pos==c[u]){
                vis[i]=0;
                ans[u]=i;
                break;
            }
            pos++;
        }
    }
    int len=v[u].size();
    rep(i,0,len-1){
        int y=v[u][i];
        dfs(y);
        siz[u]+=siz[y];
    }
    if(c[u]>siz[u]-1){
        flag=1;
        return;
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>n){
        memset(vis,0,sizeof(vis));
        memset(siz,0,sizeof(siz));
        rep(i,1,n){
            v[i].clear();
            vis[i]=i;
        }
        int root;
        rep(i,1,n){
            cin>>x>>c[i];
            if(!x)root=i;
            else v[x].push_back(i);
        }
        flag=0;
        dfs(root);
        if(flag){
            cout<<"NO"<<endl;
            continue;
        }
        cout<<"YES"<<endl;
        rep(i,1,n){
            cout<<ans[i]<<" ";
        }cout<<endl;
    }
    return 0;
}

插入法

By smilestruggler, contest: Codeforces Round #612 (Div. 2), problem: (D) Numbers on Tree, Accepted, #
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
const int MAXN = 2e3+5;
int n,x,c[MAXN],vis[MAXN],ans[MAXN],siz[MAXN],flag;
vector<int>v[MAXN],g;
void dfs(int u){
    if(flag)return;
    int len=v[u].size();
    siz[u]=1;
    rep(i,0,len-1){
        int y=v[u][i];
        dfs(y);
        siz[u]+=siz[y];
    }
    if(c[u]>siz[u]-1){
        flag=1;
        return;
    }
    if(flag)return;
    g.insert(g.begin()+c[u],u);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>n){
        memset(vis,0,sizeof(vis));
        memset(siz,0,sizeof(siz));
        rep(i,1,n){
            v[i].clear();
            vis[i]=i;
        }
        g.clear();
        int root;
        rep(i,1,n){
            cin>>x>>c[i];
            if(!x)root=i;
            else v[x].push_back(i);
        }
        flag=0;
        dfs(root);
        if(flag){
            cout<<"NO"<<endl;
            continue;
        }
        cout<<"YES"<<endl;
        rep(i,0,n-1){
            ans[g[i]]=i+1;
        }
        rep(i,1,n){
            cout<<ans[i]<<" ";
        }cout<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值