给树染色
给出一棵有n个节点的树,第i个点的权值为\(a_i\),根节点为r,现在你需要给所有点染色,一个点能够被染色,当且仅当它的父亲节点已经被染色(当然,根节点除外,可以直接被染色),而且对于一个点i染色的费用为\(\text{第几次染色}\times a_i\),询问最少的染色费用,\(n\leq 1000\)。
解
注意到一个性质,就是当树中点权最大的点q的父亲被染色后,q会马上被染色,否则替换成另外一个点w,那么有\(a_w<a_q\),设这是第\(e\)次染色,显然这两次染色的交换不会影响到e次以前的和\(e+2\)次染色以后的染色,于是知道q先染色的对答案的影响为\(e\times a_q+(e+1)\times a_w\),w先染色的对答案的影响为\(e\times a_w+a_q(e+1)\),后者与前者做差,有\(a_q-a_w>0\),于是q先染色会更加优秀。
再琢磨一下这个性质,q的父亲被染色了,那么q就会立刻被染色,这类似于两个点被强制捆绑在了一起,不妨进一步研究捆绑的性质,猜测捆绑以后的点权,设有点\(x,y,z\),设y,z已经被捆绑在了一起,那么对答案的影响只有两种可能(因为前面的染色次数,对大小关系的比较没有影响,第一段已经间接说明了这一点,一下全部省去),
\[a_x+2a_y+3a_z\text{和}a_y+2a_z+3a_x\]
同样想着构造y,z同样的形式构成一个整体(因为最后点权得是一个固定的数字)和\(x+2x+3x+...\)(因为这样才能给出显然的证明)。
因为要构造y,z一个整体,想到两边同时减z,有
\[a_x+2(a_y+a_z)\text{和}a_y+a_z+3a_x\]
要构造\(x+2x+3x+...\),就想到同时加上一个\(a_x\),再除以2以后有
\[a_x+2\frac{a_y+a_z}{2}\text{和}\frac{a_y+a_z}{2}+2a_x\]
这显然告诉我们y,z可以合并成一个点权为\(\frac{a_y+a_z}{2}\)的点,进一步推广,假设有u个点v被捆绑在一起,不妨假设这些点为\(1,2,...,u\),其中数字的顺序就是染色的顺序,再与点x考虑,只有两种情况
\[a_1+2a_2+...+ua_u+(u+1)x\]
\[x+2a_1+3a_2+...+(u+1)a_u\]
同样加上\(-a_2-2a_3...-(u-1)a_u+(u-1)x\),再除以u,有
\[\frac{a_1+a_2+...+a_u}{u}+2x\]
\[x+\frac{a_1+a_2+...+a_u}{u}\]
显然这就代表了\(1,2,,,..u\)这些点构成了一个整体,点权为\(\frac{\sum_{i=1}^ua_i}{u}\)。
于是总上,我们有做法,每次在树中找到点权最大的点,然后将它与它的父亲合并,更改一下点权,并趁机记录合并后点中的染色的顺序,最后树只会剩下一个点,那么这个点的染色顺序也就是所求,依次为依据手动模拟一下就可算出答案,时间复杂度\(O(n^2)\)。
后话:因为每次寻找到一个点权最大的点,这好像可以用优先队列,但是需要和父亲合并,也就是要支持动态修改,于是萎了,然后考虑用平衡树,需要找到父亲很麻烦,而且点权都相同的话,查找可以到\(O(n)\),故不想写了,如果各位像sxr一样强的人有办法,请联系我。
参考代码:
#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define Size 1500
#define swap(x,y) x^=y^=x^=y
using namespace std;
struct point{
point *next;int to;
}*pt,*head[Size];
int pa[Size],a[Size],ans[Size],
tot;
void dfs(int);
il void read(int&),link(int,int);
int main(){
int n,r,gzy(0);read(n),read(r);
for(int i(1);i<=n;++i)read(a[i]);
for(int i(1),u,v;i<n;++i)
read(u),read(v),link(u,v),pa[v]=u;dfs(r);
for(int i(1),j;i<=n;++i)
for(j=1;j<n;++j)
if(a[ans[j]]<a[ans[j+1]]&&pa[ans[j+1]]!=ans[j])
swap(ans[j],ans[j+1]);
for(int i(1);i<=n;++i)
gzy+=a[ans[i]]*i;printf("%d",gzy);
return 0;
}
void dfs(int x){ans[++tot]=x;
for(point *i(head[x]);
i!=NULL;i=i->next)dfs(i->to);
}
il void link(int u,int v){
pt=new point,pt->to=v;;
pt->next=head[u],head[u]=pt;
}
il void read(int &x){
x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}
原文地址:https://www.cnblogs.com/a1b3c7d9/p/11217617.html
- github搭建个人网站
- Android:一个高效的UI才是一个拉风的UI
- 什么是ORM?为什么用ORM?浅析ORM的使用及利弊
- .NET[C#]中实现实体对象深拷贝(克隆/复制)的几种方法
- Android中图片大小和屏幕密度的关系讲解
- C# WINFORM通过委托和事件窗体间(跨窗体)传值(自定义事件参数)--实例详解
- Apache Spark 2.0预览:机器学习模型持久性
- 推荐一个简单、轻量、功能非常强大的C#/ASP.NET定时任务执行管理器组件–FluentScheduler
- 携程Android App的插件化和动态加载框架
- Spring Boot构建RESTful API与单元测试
- Volley解析之表单提交篇
- JAVA中重写equals()方法的同时要重写hashcode()方法
- 调用CodeSmith类库实现代码生成(含源码)
- 1分钟生成Net对象的注释
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Qt开源作品34-qwt无需插件源码
- Qt开源作品35-秘钥生成器
- Qt开源作品36-程序守护进程
- Qt开源作品37-网络中转服务器
- Qt编写安防视频监控系统27-GPU显示
- Qt编写安防视频监控系统28-摄像机点位
- Qt编写安防视频监控系统29-掉线重连
- Qt编写安防视频监控系统30-GPS运动轨迹
- Qt编写安防视频监控系统31-onvif设备搜索
- Qt编写安防视频监控系统32-onvif信息获取
- Qt编写安防视频监控系统33-onvif云台控制
- Qt编写安防视频监控系统34-onvif事件订阅
- Qt编写安防视频监控系统35-onvif抓拍图片
- Qt音视频开发1-vlc解码播放
- 【TBase开源版测评】分布式数据自动shard分片