• 【Luogu】UVA1205 Color a Tree / SP3912 MTREECOL - Color a tree


    一道经典贪心。


    本题的贪心策略是:令 a i a_i ai 表示节点 i i i 的权值, f a i fa_i fai 表示节点 i i i 的父亲,可以发现,假如 f a i fa_i fai 被染色了,且 a i ≥ ∀ a j a_i\ge ∀a_j aiaj 且不为根节点,则接下来必定染 a i a_i ai

    既然规定了顺序,我们就可以将 f a i fa_i fai i i i 合并,用并查集维护更方便些。

    但是在处理节点之间的比较时,我们需要进行一些特殊处理。

    在《算进》中,其提到一种“等效权值”,令 s u m sum sum 为该点包含的原始权值总和, n u m num num 为该点包含的原始点数,其定义为 s u m n u m \frac{sum}{num} numsum。但是如果直接用此比较大小,会出现浮点误差。

    因此,我们选择规避掉。

    明显地,设两个节点为 x , y x,y x,y,则有 s u m x n u m x \frac{sum_x}{num_x} numxsumx s u m y n u m y \frac{sum_y}{num_y} numysumy,两者乘上 n u m x × n u m y num_x\times num_y numx×numy,得 s u m x × n u m y sum_x \times num_y sumx×numy s u m y × n u m x sum_y \times num_x sumy×numx

    于是这个处理就结束了。

    具体见代码。

    AC Code:

    #include
    #define int long long
    using namespace std;
    
    struct node{
    	int sum,num,fa,ans;//sum,num 意义见上,fa 表示该节点的父亲,ans 表示到当前节点为止的答案
    }a[1005];
    int n,r,fa[1005];
    
    bool operator<(const node &x,const node &y)//比较
    {
    	return x.sum*y.num<y.sum*x.num;
    }
    
    int find(int x)//并查集的 find()
    {
    	return fa[x]==x?x:fa[x]=find(fa[x]);
    }
    
    signed main()
    {
    	ios::sync_with_stdio(0);
    	cin>>n>>r;
    	while(n&&r)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			cin>>a[i].sum;
    			a[i].num=1;
    			fa[i]=i;
    			a[i].ans=a[i].sum;//初始化
    		}
    		for(int i=1;i<n;i++)
    		{
    			int x,y;
    			cin>>x>>y;
    			a[y].fa=x;
    		}
    		int t=n;//t 为节点数
    		while(t>2)//当结点数还未合并到两个时执行
    		{
    			node maxn={0,1,0};//找最大非根子节点
    			int j=0;
    			for(int i=1;i<=n;i++)
    			{
    				int x=find(i);
    				if(x==r) continue;//为根节点
    				if(maxn<a[x]) maxn=a[x],j=x;
    			}
    			int k=find(maxn.fa);
    			a[k].sum+=a[j].sum;//合并时相应值也要合并
    			a[k].ans+=a[k].num*a[j].sum+a[j].ans;//处理答案
    			a[k].num+=a[j].num;
    			fa[j]=k;//合并节点
    			t--;
    		}
    		if(t==1) cout<<a[1].ans<<endl;//单个根节点的特判
    		else
    		{
    			int j=0;
    			for(int i=1;i<=n;i++)
    			{
    				int x=find(i);
    				if(x==r) continue;
    				j=x;
    				break;
    			}
    			cout<<a[r].num*a[j].sum+a[j].ans+a[r].ans<<endl;//最后将答案合并输出
    		}
    		cin>>n>>r;
    	}
     	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
  • 相关阅读:
    Spring MVC上篇
    【智能合约安全】智能合约安全审计之Code4rena(或C4) \如何成为智能合约审计员
    uniapp踩坑小伎俩记录
    Redis7学习笔记01
    Acrel-2000Z电力监控系统在重庆五桂堂历史文化商业街区的应用
    redis常用命令
    matlab 七段式轨迹 S型速度规划
    阿里面试,HashMap与Redis哈希结构扩容的区别
    工具及方法 - 多邻国: Duolingo
    linux的Java运行
  • 原文地址:https://blog.csdn.net/Leo_Chenjy/article/details/126036264