【codeforces contest 1119 F】Niyaz and Small Degrees
题目
描述
\(n\) 个点的树,每条边有一个边权;
对于一个 \(X\) ,求删去一些边后使得每个点的度数 \(d_i\) 均不超过 \(X\) 的最小代价;
你需要依次输出 \(X=0 \to n-1\) 的答案;
范围
$ 1 \le n \le 250000 $
题解
考虑对于一个\(X\)怎么做,设 $ dp_{i,0/1} $ 表示 $ u $ 节点的子树,$ i $连向父亲的边是否被删且度数不超过 $ X $ 的最小代价。转移时将 \(dp_{v,1} + w(u,v) - dp_{v,0}\) 排序,小于 \(0\) 的优先选,再补到 $ d_{u} - X (-1) $ 个即可 。
复杂度 : \(O(n^2)\)
注意到 \(\sum_{i=0}^{n-1}\sum_{j=1}^{n} [d_j>i] = \sum_{i=1}^{n} d_{j} = 2n-2 = O(n)\) 。
升序考虑\(X\),当一个点的度数小于等于\(X\)的时候可以直接删去这个点并把这个点的贡献加入相邻的点中;
可以用一个支持删除的堆或者set实现。再执行同样的\(dp\);
但是直接维护所有点仍是\(O(n^2log \ n )\)的,需要把复杂度降到只和当前保留的点有关。
注意到对于一个 $ u $ , $ d_{u}-X(-1) $ 在减小,维护堆中所有要选的$ d_{u}-X( -1 ) $个点的 $ sum $ ,这样子每次转移就只需要个新加儿子的\(dp\)值,然后调整堆的\(size\)即可,转移完后最后再还原;
这样子转移复杂度就只和度数\(>=X\)的点有关;
复杂度:\(O(n \log n)\) ;
#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define mk make_pair
#define ll long long
using namespace std;
const int N=250010;
int n,D,vis[N],d[N],nxt[N],st[N];
ll sum[N],ans,f[N][2];
typedef pair<int,int>pii;
vector<pii>g[N];
vector<int>vec[N];
int cnt;
bool cmp(const pii&a,const pii&b){
return d[a.fi]<d[b.fi];
}
struct data{
priority_queue<ll>A,B;
void push(ll x){
// cnt++;
A.push(x);
}
void del(ll x){
// cnt++;
B.push(x);
}
int top(){
while(!B.empty()&&A.top()==B.top())A.pop(),B.pop();
return A.top();
}
void pop(){
top();A.pop();
}
int size(){
return A.size()-B.size();
}
bool empty(){
return A.size()==B.size();
}
}q[N];
void update(int u){
vis[u]=1;
for(int i=0;i<g[u].size();++i){
int v=g[u][i].fi,w=g[u][i].se;
if(vis[v])continue;
q[v].push(w),sum[v]+=w;
}
}
void resize(int u,int num){
while(q[u].size()>num){
sum[u]-=q[u].top();
q[u].pop();
}
}
void resize(int u,int num,vector<ll>&add){
while(q[u].size()>num){
sum[u]-=q[u].top();
add.pb(q[u].top());
q[u].pop();
}
}
void dfs(int u,int F){
/*{
cnt++;
}*/
vis[u]=1;
int num=d[u]-D;
resize(u,num);
vector<ll>add,del;
ll all=0;
while(st[u]<g[u].size()&&d[g[u][st[u]].fi]<=D)st[u]++;
for(int i=st[u];i<g[u].size();++i){
int v=g[u][i].fi,w=g[u][i].se;
if(vis[v]||v==F)continue;
dfs(v,u);
if(f[v][1]+w<=f[v][0])num--,all+=f[v][1]+w;
else {
all+=f[v][0];
ll tmp=f[v][1]+w-f[v][0];
q[u].push(tmp),sum[u]+=tmp;
del.pb(tmp);
}
}
resize(u,max(0,num),add);
f[u][0]=all+sum[u];
resize(u,max(0,--num),add);
f[u][1]=all+sum[u];
for(auto x : add)q[u].push(x),sum[u]+=x;
for(auto x : del)q[u].del(x),sum[u]-=x;
}
int main(){
//freopen("F.in","r",stdin);
//freopen("F.out","w",stdout);
scanf("%d",&n);
for(int i=1,u,v,w;i<n;++i){
scanf("%d%d%d",&u,&v,&w);
g[u].pb(mk(v,w));
g[v].pb(mk(u,w));
d[u]++,d[v]++;
ans+=w;
}
for(int i=1;i<=n;++i){
vec[d[i]].pb(i);
sort(g[i].begin(),g[i].end(),cmp);
}
nxt[n]=n+1;
for(int i=n-1;i;--i){
if(vec[i+1].size())nxt[i]=i+1;
else nxt[i]=nxt[i+1];
}
printf("%I64d ",ans);
for(int i=1;i<n;++i){
for(auto j : vec[i])update(j);
ans=0;D=i;
for(int j=i+1;j<=n;j=nxt[j])
for(auto k : vec[j])if(!vis[k]){
dfs(k,0);ans+=f[k][0];
}
for(int j=i+1;j<=n;j=nxt[j])
for(auto k : vec[j]){
vis[k]=0;
}
printf("%I64d ",ans);
}
//cerr<<fixed<<setprecision(10)<<1.0*clock()/CLOCKS_PER_SEC<<endl;
return 0;
}
【codeforces contest 1119 F】Niyaz and Small Degrees的更多相关文章
- [codeforces contest 1119 F] Niyaz and Small Degrees 解题报告 (树形DP+堆)
interlinkage: http://codeforces.com/contest/1119/problem/F description: 有一颗$n$个节点的树,每条边有一个边权 对于一个$x$ ...
- 【cf contest 1119 H】Triple
题目 给出 \(n\) 个三元组\(\{ a_i,b_i,c_i \}\)和\(x,y,z\): 将每个三元组扩展成(\(x\)个\(a_i\),\(y\)个\(b_i\),\(z\)个\(c_i\) ...
- 【cf contest 1119 G】Get Ready for the Battle
题目 你有\(n\)个士兵,需要将他们分成\(m\)组,每组可以为0: 现在这些士兵要去攻打\(m\)个敌人,每个敌人的生命值为\(hp_i\) : 一轮游戏中一组士兵选定一个攻打的敌人,敌人生命值- ...
- 【Codeforces #312 div2 A】Lala Land and Apple Trees
# [Codeforces #312 div2 A]Lala Land and Apple Trees 首先,此题的大意是在一条坐标轴上,有\(n\)个点,每个点的权值为\(a_{i}\),第一次从原 ...
- 【Educational Codeforces Round 37 F】SUM and REPLACE
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 那个D函数它的下降速度是很快的. 也就是说到最后他会很快的变成2或者1 而D(2)==2,D(1)=1 也就是说,几次操作过后很多数 ...
- 【AtCoder Regular Contest 082 F】Sandglass
[链接]点击打开链接 [题意] 你有一个沙漏. 沙漏里面总共有X单位的沙子. 沙漏分A,B上下两个部分. 沙漏从上半部分漏沙子到下半部分. 每个时间单位漏1单位的沙子. 一开始A部分在上面.然后在r1 ...
- 【Atcoder Grand Contest 011 F】Train Service Planning
题意:给\(n+1\)个站\(0,\dots,n\),连续的两站\(i-1\)和\(i\)之间有一个距离\(A_i\),其是单行(\(B_i=1\))或双行(\(B_i=2\)),单行线不能同时有两辆 ...
- 【AtCoder Regular Contest 076 F】Exhausted (贪心)
Description 机房里有M台电脑排成一排,第i台电脑的坐标是正整数i. 现在有N个OIer进入了机房,每个OIer需要一台电脑来学tui习ji,同时每个OIer对自己电脑所处的坐标范围有一个要 ...
- 【郑轻邀请赛 F】 Tmk吃汤饭
[题目链接]:https://acm.zzuli.edu.cn/zzuliacm/problem.php?id=2132 [题意] [题解] 很容易想到用队列来模拟; 这个队列维护的是正在煮的4个人煮 ...
随机推荐
- 运维中的日志切割操作梳理(Logrotate/python/shell脚本实现)
对于Linux系统安全来说,日志文件是极其重要的工具.不知为何,我发现很多运维同学的服务器上都运行着一些诸如每天切分Nginx日志之类的CRON脚本,大家似乎遗忘了Logrotate,争相发明自己的轮 ...
- Gerrit日常维护记录
Gerrit代码审核工具是个好东西,尤其是在和Gitlab和Jenkins对接后,在代码控制方面有着无与伦比的优势. 在公司线上部署了一套Gerrit系统,在日常运维中,使用了很多gerrit命令,在 ...
- python之requests
发送请求 导入 Requests 模块: >>> import requests >>> r = requests.get('https://xxxxxxx.jso ...
- php 多个文件压缩到一起存储
$zip = new ZipArchive();$res = $zip->open('test.zip', ZipArchive::CREATE); //不存在则创建$filepath = 's ...
- Pair Project1:电梯控制程序
12061199 程刚 && 12061204 黎柱金 一.结对编程的优缺点 结对编程相对于一个人的编程有更多的优点,缺点也有很大不同. 首先,优点: 结队可以让两人可以更好的协 ...
- Asp.net框架与SpringMvc框架简单分析
(此文为自我总结,错误很多请勿借鉴) 1.就前两天分析SpringMvc框架中是通过controler来实现跳转页面,通过mapping来实现数据连接 分析的方法又通过java的类之间进行相互调用,个 ...
- 语音笔记:MFCC
一,传统语音识别体系结构 二,MFCC特征提取 MFCC(Mel-frequency cepstral coefficients):梅尔频率倒谱系数.梅尔频率是基于人耳听觉特性提出来的, 它与Hz频率 ...
- SE Springer小组之《Spring音乐播放器》可行性研究报告五、六
5 可选择的其他系统方案 曾经考虑过制作闹钟系统,但考虑到闹钟系统在电脑应用中极其不实用,所以此方案未通过. 6 投资及效益分析 6.1支出 本软件只用于完成课程学习要求,不用做商用,无基础设备等支出 ...
- 在web.xml中配置监听器来控制ioc容器生命周期
5.整合关键-在web.xml中配置监听器来控制ioc容器生命周期 原因: 1.配置的组件太多,需保障单实例 2.项目停止后,ioc容器也需要关掉,降低对内存资源的占用. 项目启动创建容器,项目停止销 ...
- 安装MySQL和其他包
安装 MySQL 1. 下载 MySQL 安装包 记得要下载 msi 可执行文件,而不是源码包. https://dev.mysql.com/downloads/file/?id=474803 这个安 ...