\(\mathcal{Description}\)

  Link.

  给定一棵 \(n\) 个结点的树,边有边权,对于每个整数 \(x\in[0,n)\),求出最少的删边代价使得任意结点度数不超过 \(x\)。

  \(n\le2.5\times10^5\)。

\(\mathcal{Solution}\)

  从单个询问入手,设此时 \(x\) 为常数,就有一个简单的树上 DP。令 \(f(u,0/1)\) 表示 \(u\) 点与父亲的边不断 / 断时,\(u\) 子树内的最小代价。以 \(f(u,0)\) 为例,设 \(v\) 是 \(u\) 的儿子,转移相当于强制选择至少 \(\max\{0,d_u-x\}\) 个 \(f(v,1)\) 进行转移。先特判掉 \(f(v,1)+\operatorname{cost}(u,v)\le f(v,0)\) 的 \(v\),此时用 \(f(v,1)+\operatorname{cost}(u,v)\) 的代价断边显然更优。此后,先仅用 \((v,0)\) 转移,把 \(f(v,1)+\operatorname{cost}(u,v)-f(v,0)\) 压入大根堆。保留堆最后 \(\max\{0,d_u-x\}\) 个元素并加入贡献就行啦。

  接下来,按 \(x\) 从 \(0\) 到 \(n-1\) 的顺序考虑询问。可以发现,在 \(x\) 增大的过程中,某些点的度数不超过 \(x\),那么 \(x\) 对这些点就再也没有限制了。那么强行把这个点拉到叶子,将它的 DP 信息压入邻接点的堆,再暴力跑 DP 就解决了。

  复杂度 \(\mathcal O(n\log n)\)。

\(\mathcal{Code}\)

#pragma GCC optimize( 2 )

#include <queue>
#include <cstdio>
#include <vector>
#include <algorithm> typedef long long LL; const int MAXN = 2.5e5;
int n, ecnt, head[MAXN + 5], deg[MAXN + 5], vis[MAXN + 5];
LL f[MAXN + 5][2];
std::pair<int, int> order[MAXN + 5];
bool kill[MAXN + 5]; inline char fgc () {
static char buf[1 << 17], *p = buf, *q = buf;
return p == q && ( q = buf + fread ( p = buf, 1, 1 << 17, stdin ), p == q ) ? EOF : *p ++;
} inline int rint () {
int x = 0; char d = fgc ();
for ( ; d < '0' || '9' < d; d = fgc () );
for ( ; '0' <= d && d <= '9'; d = fgc () ) x = x * 10 + ( d ^ '0' );
return x;
} inline void wint ( const LL x ) {
if ( 9 < x ) wint ( x / 10 );
putchar ( x % 10 ^ '0' );
} std::vector<LL> pmem, rmem;
std::vector<std::pair<int, int> > gr[MAXN + 5]; struct RemovableHeap {
int siz; LL sum;
std::priority_queue<LL> ele, rem;
RemovableHeap (): siz ( 0 ), sum ( 0 ) {}
inline void snap () { pmem.clear (), rmem.clear (); }
inline void nspush ( const LL x ) { ++ siz, sum += x, ele.push ( x ); }
inline void nspop ( const LL x ) { -- siz, sum -= x, rem.push ( x ); }
inline void nspop () { -- siz, sum -= top (), ele.pop (); }
inline void push ( const LL x ) { nspush ( x ), pmem.push_back ( x ); }
inline void pop ( const LL x ) { nspop ( x ), rmem.push_back ( x ); }
inline void pop () { -- siz, sum -= top (), rmem.push_back ( top () ), ele.pop (); }
inline int size () { return siz; }
inline LL top () {
for ( ; ! ele.empty () && ! rem.empty () && ele.top () == rem.top (); ele.pop (), rem.pop () );
return ele.top ();
}
inline void recov () {
for ( LL p: pmem ) nspop ( p );
for ( LL r: rmem ) nspush ( r );
pmem.clear (), rmem.clear ();
}
} heap[MAXN + 5]; inline void solve ( const int u, const int x, const int fa ) {
vis[u] = x; int wait = deg[u] - x;
for ( ; heap[u].size () > wait; heap[u].nspop () );
for ( auto v: gr[u] ) {
if ( v.first == fa ) continue;
if ( deg[v.first] <= x ) break;
solve ( v.first, x, u );
}
heap[u].snap ();
LL bas = 0;
for ( auto v: gr[u] ) {
if ( v.first == fa ) continue;
if ( deg[v.first] <= x ) break;
LL dt = f[v.first][1] + v.second - f[v.first][0];
if ( dt <= 0 ) { -- wait, bas += f[v.first][1] + v.second; continue; }
bas += f[v.first][0], heap[u].push ( dt );
}
for ( ; heap[u].size () && heap[u].size () > wait; heap[u].pop () );
f[u][0] = bas + heap[u].sum;
for ( ; heap[u].size () && heap[u].size () > wait - 1; heap[u].pop () );
f[u][1] = bas + heap[u].sum;
heap[u].recov ();
} int main () {
n = rint ();
LL ws = 0;
for ( int i = 1, u, v, w; i < n; ++ i ) {
u = rint (), v = rint (), w = rint ();
++ deg[u], ++ deg[v], ws += w;
gr[u].push_back ( { v, w } );
gr[v].push_back ( { u, w } );
}
wint ( ws );
for ( int i = 1; i <= n; ++ i ) {
order[i] = { deg[i], i };
sort ( gr[i].begin (), gr[i].end (),
[]( const std::pair<int, int> a, const std::pair<int, int> b ) {
return deg[a.first] > deg[b.first];
}
);
}
std::sort ( order + 1, order + n + 1 );
for ( int x = 1, dis = 1; x < n; ++ x ) {
for ( int u; dis <= n && order[dis].first == x; ++ dis ) {
kill[u = order[dis].second] = true;
for ( auto v: gr[u] ) {
if ( deg[v.first] <= x ) break;
heap[v.first].nspush ( v.second );
}
}
LL ans = 0;
for ( int i = dis, u; i <= n; ++ i ) {
if ( vis[u = order[i].second] ^ x ) {
solve ( u, x, 0 );
ans += f[u][0];
}
}
putchar ( ' ' ), wint ( ans );
}
putchar ( '\n' );
return 0;
}

Solution -「CF 1119F」Niyaz and Small Degrees的更多相关文章

  1. Solution -「CF 1342E」Placing Rooks

    \(\mathcal{Description}\)   Link.   在一个 \(n\times n\) 的国际象棋棋盘上摆 \(n\) 个车,求满足: 所有格子都可以被攻击到. 恰好存在 \(k\ ...

  2. Solution -「CF 1622F」Quadratic Set

    \(\mathscr{Description}\)   Link.   求 \(S\subseteq\{1,2,\dots,n\}\),使得 \(\prod_{i\in S}i\) 是完全平方数,并最 ...

  3. Solution -「CF 923F」Public Service

    \(\mathscr{Description}\)   Link.   给定两棵含 \(n\) 个结点的树 \(T_1=(V_1,E_1),T_2=(V_2,E_2)\),求一个双射 \(\varph ...

  4. Solution -「CF 923E」Perpetual Subtraction

    \(\mathcal{Description}\)   Link.   有一个整数 \(x\in[0,n]\),初始时以 \(p_i\) 的概率取值 \(i\).进行 \(m\) 轮变换,每次均匀随机 ...

  5. Solution -「CF 1586F」Defender of Childhood Dreams

    \(\mathcal{Description}\)   Link.   定义有向图 \(G=(V,E)\),\(|V|=n\),\(\lang u,v\rang \in E \Leftrightarr ...

  6. Solution -「CF 1237E」Balanced Binary Search Trees

    \(\mathcal{Description}\)   Link.   定义棵点权为 \(1\sim n\) 的二叉搜索树 \(T\) 是 好树,当且仅当: 除去最深的所有叶子后,\(T\) 是满的: ...

  7. Solution -「CF 623E」Transforming Sequence

    题目 题意简述   link.   有一个 \(n\) 个元素的集合,你需要进行 \(m\) 次操作.每次操作选择集合的一个非空子集,要求该集合不是已选集合的并的子集.求操作的方案数,对 \(10^9 ...

  8. Solution -「CF 1023F」Mobile Phone Network

    \(\mathcal{Description}\)   Link.   有一个 \(n\) 个结点的图,并给定 \(m_1\) 条无向带权黑边,\(m_2\) 条无向无权白边.你需要为每条白边指定边权 ...

  9. Solution -「CF 599E」Sandy and Nuts

    \(\mathcal{Description}\)   Link.   指定一棵大小为 \(n\),以 \(1\) 为根的有根树的 \(m\) 对邻接关系与 \(q\) 组 \(\text{LCA}\ ...

随机推荐

  1. 微信小程序组建通信

    子组件传递父组件需要用到triggerEvent方法,当子组件(自定义组件)点击button的时候调用triggerEvent方法传递一些数据,首先第一个属性为自定义名称(myevent)对应父组件( ...

  2. Linux上天之路(十)之Linux磁盘管理

    主要内容 磁盘介绍 磁盘管理 磁盘限额 逻辑卷管理 磁盘阵列 1. 磁盘介绍 硬盘最基本的组成部分是由坚硬金属材料制成的涂以磁性介质的盘片,不同容量硬盘的盘片数不等.每个盘片有两面,都可记录信息.盘片 ...

  3. CSS命名规范整理

    基于网易NEC修改后,整理的命名规范 单行写完一个选择器定义 便于选择器的寻找和阅读,也便于插入新选择器和编辑,便于模块等的识别.去除多余空格,使代码紧凑减少换行. 如果有嵌套定义,可以采取内部单行的 ...

  4. 【PTA】5-1 输入一个正整数n,再输入n个学生的姓名和百分制成绩,将其转换为两级制成绩后输出。

    5-1 输入一个正整数n,再输入n个学生的姓名和百分制成绩,将其转换为两级制成绩后输出.要求定义和调用函数set_grade(stu, n),其功能是根据结构数组stu中存放的学生的百分制成绩scor ...

  5. #pragma pack() -----设置默认对齐数

    #pragma pack()  -----设置默认对齐数 预处理命令#pragma:程序如下 则根据修改的对齐数来算:则需要占据内存的大小是14 如果不进行设置,则按照编译器默认的对齐数来算:则需要占 ...

  6. C# 获取DPI例子

    public static float GetDpiX() { System.Windows.Forms.Panel p = new System.Windows.Forms.Panel(); Sys ...

  7. centos,fedora,ubuntu,opensuse感想

    最先接触的是ubuntu,貌似身边同学用的最多,出了什么问题,网上解决方法也很多,但是后来学鸟哥私房菜,就按照他说的在实验室电脑装了一个centos,果然为服务器而生,稳定得很,界面朴素,装日常软件比 ...

  8. 如何在Xamarin中快速集成Android版认证服务-手机号码篇

    Xamarin作为微软提供的移动服务多系统开发平台,成为很多开发者首选的应用开发平台.AppGallery Connect(以下简称AGC)也在逐步的支持Xamarin的SDK.认证服务也是支持Xam ...

  9. golang中的接口值

    package main import ( "bytes" "fmt" "io" ) // 此处的w参数默认是一个空接口,当传递进来buf参 ...

  10. a 标签一些特殊用法

    发邮件 <a href="mailto:youemail@mail.com?subject=邮件标题&body=邮件内容">告诉我们</a> 打电话 ...