兔子与樱花

一、题目

点此看题

二、解法

我们发现删去一个点就会给它的父节点造成 s o n [ v ] + c [ v ] − 1 son[v]+c[v]-1 son[v]+c[v]1的负担。
我们遍历整棵树,贪心地看,我们选择删去的子节点的负担越小就越优,于是我们从小到大地删除子节点。
但是增加了当前点的负担也给当前点的父亲的操作造成影响,那我们的贪心是错误的吗?其实更新当前点的更新对答案的贡献至少为1,而对于父节点的操作贡献影响至多为1,所以我们贪心出来的操作一定是最优的,这里的讨论可以类比2018年提高组D1T3。

#include 
#include 
#include 
using namespace std;
const int MAXN = 2000005;
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,m,tot,ans,f[MAXN],son[MAXN],c[MAXN];
struct edge
{
    int v,next;
}e[2*MAXN];
struct node
{
    int u,c;
    bool operator < (const node &B) const {
        return c<B.c;
    }
};
void dfs(int u,int fa)
{
    vector<node> ve;
    for(int i=f[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(v==fa) continue;
        dfs(v,u);
        son[u]++;
        ve.push_back(node{v,son[v]+c[v]});
    }
    sort(ve.begin(),ve.end());
    for(int i=0;i<ve.size();i++)
    {
        int v=ve[i].u;
        if(c[u]+son[u]+ve[i].c-1<=m)
        {
            c[u]+=c[v];
            son[u]+=son[v]-1;
            ans++;
        }
        else break;
    }
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)
        c[i]=read();
    for(int i=1;i<=n;i++)
    {
        int k=read();
        for(int j=1;j<=k;j++)
        {
            int v=read()+1;
            e[++tot]=edge{v,f[i]},f[i]=tot;
            e[++tot]=edge{i,f[v]},f[v]=tot;
        }
    }
    dfs(1,0);
    printf("%d\n",ans);
}

你可能感兴趣的:(贪心,树形dp)