zoukankan      html  css  js  c++  java
  • CF1336 Linova and Kingdom

    题面

    给定 n 个节点的有根树,根是 1 号节点。

    你可以选择 k 个节点将其设置为工业城市,其余设置为旅游城市。

    对于一个工业城市,定义它的幸福值为工业城市到根的路径经过的旅游城市的数量。

    你需要求出所有工业城市的幸福值之和的最大可能值。

    (1<=k<=n<=2*10^5);

    传送门

    题解

    仔细想想不难想到,肯定是从叶子节点这种深度大的选
    然后做法就是排遍序,然后按深度从大到小选???

    但是,真的是这样吗???

    我们仔细考虑一个点作为工业城市的条件与情况:

    首先,假如以此点为根的子树中还有点没有被选,那么这一个点肯定不是当前最优决策
    有了上面这一条,我们可以推出:

    一个点被选工业城市的前提是它的儿子都已经被选了

    然后我们来考虑将一个点选为工业城市的贡献:

    • 它的深度
    • 它会使得它的子树中每个已被选为工业城市的点贡献-1

    又因为该点子树中肯定每个点都已经是工业城市,所以该点贡献value就是:
    (value=它的深度-子树大小)

    按照这个(value)维护一个大根堆,将每个儿子都已被选或本身为叶子节点的点插入就好

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define re register
    #define in inline
    #define ll long long
    #define get getchar()
    in int read()
    {
        int t=0,x=1;char ch=get;
        while((ch<'0'||ch>'9')&&ch!='-')ch=get;
        if(ch=='-')x=-1,ch=get;
        while(ch<='9'&&ch>='0')t=t*10+ch-'0',ch=get;
        return x*t;
    }
    const int _=2e6+5;
    struct edge{
    	int to,ne;
    }e[_];
    struct dian{
    	int id,deep;
    }d[_];
    int h[_],n,k,num[_],tot,father[_];
    in void add(int x,int y)
    {
    	e[++tot].to=y,e[tot].ne=h[x],h[x]=tot;
    }
    int val[_],siz[_],len[_];
    in void dfs(int u,int fa)
    {
    	d[u].id=u,d[u].deep=d[fa].deep+1;
    	siz[u]=1;father[u]=fa;
    	for(re int i=h[u];i;i=e[i].ne)
    	{
    		int v=e[i].to;
    		if(v==fa) continue;
    		dfs(v,u);
    		siz[u]+=siz[v];
    	}
    }
    in int cmp(dian a,dian b)
    {
    	return a.deep>b.deep;
    }
    in ll work(int x)
    {
    	return (d[x].deep-1)-siz[x]+1;
    }
    priority_queue<pair<ll,int> >q;
    in void dfs2(int u,int fa)
    {
    	len[u]+=len[fa];
    	if(!val[u]) len[u]++;
    	for(re int i=h[u];i;i=e[i].ne)
    	{
    		int v=e[i].to;
    		if(v==fa) continue;
    		dfs2(v,u);
    	}
    }
    int main()
    {
    	n=read(),k=read();
    	for(re int i=1;i<n;i++)
    	{
    		int x=read(),y=read();
    		add(x,y),add(y,x);
    		num[x]++,num[y]++; //统计每个点的度数,之后要统计哪些点成为"叶子"节点
    	}
    	dfs(1,0); //求深度、子树大小、父亲节点编号
    	for(re int i=1;i<=n;i++)
    		if(num[i]==1)
    			q.push(make_pair(work(i),i)); //把真正的叶子加入优先队列
    	while(k--)
    	{
    		int u=q.top().second;q.pop();
    		val[u]=1;num[father[u]]--; //val[i]为1 表示此点已是工业城市;将父亲节点的度数-1
    		if(1==num[father[u]]) //若父亲节点度数为1,则说明父亲节点的所有儿子都已经成为了工业城市,所以该父亲节点也有了“候选资格”
    			q.push(make_pair(work(father[u]),father[u]));
    	}
    	dfs2(1,0); //统计每个工业城市的答案
    	ll ans=0;
    	for(re int i=1;i<=n;i++)
    		if(val[i])
    			ans+=len[i];
    	cout<<ans<<endl;
     	return 0;
    }
    
    
  • 相关阅读:
    Linux crontab 命令
    tcpdump抓包工具
    tcpdump过滤某个端口
    ARM处理器基础Cortex-M4
    rtems floating poing switch
    ARM处理器的堆栈和函数调用,以及与Sparc的比较
    关于调用堆栈,任务堆栈
    如何测试嵌入式处理器的CPU使用率
    关于嵌入式实时操作系统的实时性
    RTEMS API
  • 原文地址:https://www.cnblogs.com/yzhx/p/12723121.html
Copyright © 2011-2022 走看看