cf 1059e 思维 贪心 树

时间:2022-03-21 21:57:54

参考博客:http://www.cnblogs.com/waldenlake/p/9750249.html

题意:将一棵n个点的带权有根树剖分成尽量少的链,使得

(1)链的两个端点是祖先关系

(2)链含有的顶点个数小于等于L

(3)链上所有点的点权和小于等于S。
求出最少链的数量,如果无解输出-1。N<=105

 

思路:

想法非常的新颖 ,根据子节点的情况来决定父节点是跟随某个子节点继续向上,还是自己独立开始一段新的path。

但是!!!想想这个问题的性质,有个疑问:局部最优会导致全局最优吗?答案是肯定的,所以,直接上贪心!

 

我们来看这个贪心的性质,就是有两个叶子节点a,b,他们有公共的父节点c,沿着a,b一直向上寻找(不同问题有不同寻找方向,一般从下网上,但别被拘束),直到长度或者w超限,假设a能到c上面的一个节点,b却能到c上面的三个节点,那么这三个节点划分到b的path为最优,因为我总不至于能包含却不去包含他们的。

就是说,即便先选了a,向上追溯并染色到了c上1点,后选了b,向上追溯并染色到了c上3点,我们不就是等于说,a的后半段不跟a了,改成属于b的path。因为我们总是改变后半段的归属,右总是从叶子节点开始,所以这种贪心总是能得到更好的结果,而且保证最好的结果是总能出现的。

 

下面的代码是可以hack的,因为没有保证每次都是从叶子节点开始寻找,只要做一次bfs对层次作排序就能保证son总是出现father后

 

\没想到div2最后一题居然这么简单的贪心就可以解决。。还是思维不够@#$%

 

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define pii pair<int,int>
#define all(v) v.begin(),v.end()

const int  N = 1E5+3;


ll w[N],p[N];
int vis[N];

int main(){

    ll n,l,s;
    cin>>n>>l>>s;
    int f=0;
    for(int i=1;i<=n;++i){
        cin>>w[i];
        if(w[i]>s)f=1;
    }
    for(int i=2;i<=n;++i)cin>>p[i];
    if(f){
        printf("-1\n");return 0;
    }
    int ans= 0 ;
    //其实还不太准确 要保证从叶子开始
    for(int i=n;i>=1;--i){
        if(vis[i]==0){
            ans++;
            int j = i;
            ll xl =l,xs= s;
            while(xs >= w[j] && xl>0 && j!=0){
                xs -= w[j];   vis[j]=1;
                xl--;
                j = p[j];
            }
        }
    }
    cout<<ans<<endl;


    return 0;
}