CF1119F Niyaz and Small Degrees
题意
给你\(n\)个点的树,边有边权
问使得所有的点度数都小于等于\(x\)的最小删边的代价 \([x \in 0...n-1]\)
题解
首先对于每个\(x\)
可以有一个\(O(nlogn)\)的做法
就是设\(f[u][0/1]\)表示不选择/选择点\(u\)的最小代价
那么就是把所有儿子按照\(f[v][1]+w-f[v][0]\)排序
然后\(f[u][0]\)就是选择至少前\(d[u]-x\)小的\(f[v][1]+w\),其他选择\(min(f[v][1]+w,f[v][0])\)
同理,\(f[u][1]\)就是选择至少前\(d[u]-x-1\)小的
其实这个可以通过对于所有儿子都是加上\(f[v][0]\)
然后把\(f[v][1]+w-f[v][0]\)加入小根堆并选择至少\(d[u]-x\)个来实现
然后我们可以发现随着\(x\)的增加,合法的点数越来越少
那么我们可以每次只考虑合法的点,把不合法的点的边权加入父节点中然后删除
然后遍历合法儿子的时候也是把\(f[v][1]+w-f[v][0]\)加入父节点中
那么问题就是找到至少前\(d[u]-x\)小的最小值的和
平衡树即可解决
每次遍历合法点的复杂度是
\(\sum_{i=0}^{n-1}\sum_{j=1}^{n}[i\le d_j]\)
然而一棵树的\(d_i\)和为\(n\times 2-2\)
所以遍历的总复杂度是\(O(n)\)
代码
#include<queue>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
const int M = 250005 ;
const LL INF = 1e16 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
int vis[M] ;
int n , num , hea[M] ;
int d[M] , pi[M] , fdis[M] , fa[M] ;
int Tag , dmx , rt[M] ;
LL ans , f[M][2] ;
struct Node { int v , w ; } ;
inline bool operator < (Node A , Node B) {
return d[A.v] > d[B.v] ;
}
vector < Node > vec[M] ;
inline bool cmp(int a , int b) {
return d[a] < d[b] ;
}
inline void add_edge(int u , int v , int w) {
vec[u].push_back((Node) { v , w }) ;
}
void fdfs(int u , int father) {
fa[u] = father ;
for(int i = 0 , v , w , sz = vec[u].size() ; i < sz ; i ++) {
v = vec[u][i].v , w = vec[u][i].w ; if(v == father) continue ;
fdis[v] = w ; fdfs(v , u) ;
}
}
namespace fhq {
# define ls (son[now][0])
# define rs (son[now][1])
int tot ;
LL sum[M * 8] , val[M * 8] ;
int size[M * 8] , pos[M * 8] , son[M * 8][2] ;
inline int New(LL w) {
size[++tot] = 1 ; pos[tot] = rand() ;
sum[tot] = w ; val[tot] = w ; return tot ;
}
inline void pushup(int now) {
size[now] = size[ls] + size[rs] + 1 ;
sum[now] = sum[ls] + sum[rs] + val[now] ;
}
int Merge(int x , int y) {
if(!x || !y) return x + y ;
if(pos[x] < pos[y]) {
son[x][1] = Merge(son[x][1] , y) ;
pushup(x) ; return x ;
}
else {
son[y][0] = Merge(x , son[y][0]) ;
pushup(y) ; return y ;
}
}
void Split(int now , LL k , int &x , int &y) {
if(!now) return (void)(x = y = 0) ;
if(val[now] <= k) {
x = now ;
Split(rs , k , rs , y) ;
}
else {
y = now ;
Split(ls , k , x , ls) ;
}
pushup(now) ;
}
inline void Insert(int &root , LL w) {
int x , y ;
Split(root , w , x , y) ;
root = Merge(Merge(x , New(w)) , y) ;
}
inline void Del(int &root , LL w) {
int x , y , z ;
Split(root , w , x , z) ;
Split(x , w - 1 , x , y) ;
y = Merge(son[y][0] , son[y][1]) ;
root = Merge(Merge(x , y) , z) ;
}
inline LL Rnk_val(int now , int k) {
while(1) {
if(k <= size[ls]) now = ls ;
else if(k == size[ls] + 1) return val[now] ;
else k -= size[ls] + 1 , now = rs ;
}
}
inline LL Kth_Sum(int now , int k) { // 找前k大元素的和
if(!k) return 0 ; LL ret = 0 ;
while(1) {
if(k <= size[ls]) now = ls ;
else if(k == size[ls] + 1) {
ret += sum[ls] + val[now] ;
return ret ;
}
else {
ret += sum[ls] + val[now] ;
k -= size[ls] + 1 ;
now = rs ;
}
}
}
}
void dfs(int u , int father) {
vis[u] = Tag ; f[u][0] = f[u][1] = 0 ;
if(d[u] <= Tag) return ;
for(int i = 0 , v , w , sz = vec[u].size() ; i < sz ; i ++) {
v = vec[u][i].v , w = vec[u][i].w ;
if(v == father) continue ;
dfs(v , u) ;
f[u][0] += f[v][0] ; f[u][1] += f[v][0] ;
fhq::Insert(rt[u] , f[v][1] + w - f[v][0]) ;
}
int cnt = 0 ; LL x , y , v ;
int l = 1 , r = fhq::size[rt[u]] , ret = 0 , mid ;
while(l <= r) { // 找有几个小于0
mid = (l + r) >> 1 ;
if(fhq::Rnk_val(rt[u] , mid) < 0) ret = mid , l = mid + 1 ;
else r = mid - 1 ;
}
if(ret <= d[u] - Tag)
f[u][0] += fhq::Kth_Sum(rt[u] , d[u] - Tag) ;
else f[u][0] += fhq::Kth_Sum(rt[u] , ret) ;
if(ret <= d[u] - Tag - 1)
f[u][1] += fhq::Kth_Sum(rt[u] , d[u] - Tag - 1) ;
else f[u][1] += fhq::Kth_Sum(rt[u] , ret) ;
for(int i = 0 , v , w , sz = vec[u].size() ; i < sz ; i ++) {
v = vec[u][i].v , w = vec[u][i].w ;
if(v == father) continue ;
fhq::Del(rt[u] , f[v][1] + w - f[v][0]) ;
}
}
int main() {
n = read() ;
for(int i = 1 , u , v , w ; i < n ; i ++) {
u = read() ; v = read() ; w = read() ;
add_edge(u , v , w) ; add_edge(v , u , w) ;
++ d[u] ; ++ d[v] ; ans += w ;
}
fdfs(1 , 0) ;
for(int i = 1 ; i <= n ; i ++) {
pi[i] = i ;
dmx = max( dmx , d[i] ) ;
sort(vec[i].begin() , vec[i].end()) ;
}
sort(pi + 1 , pi + n + 1 , cmp) ;
printf("%lld ",ans) ;
for(int x = 1 , Now = 1 ; x < n ; x ++) {
Tag = x ; ans = 0 ;
while(Now < n && d[pi[Now]] <= x) {
f[pi[Now]][0] = 0 ;
f[pi[Now]][1] = 0 ;
++ Now ;
}
for(int j = Now ; j <= n ; j ++) {
int v ;
while(!vec[pi[j]].empty()) {
v = vec[pi[j]][vec[pi[j]].size() - 1].v ;
if(d[v] <= x) {
if(pi[j] == fa[v])
fhq::Insert( rt[pi[j]] , fdis[v] ) ;
vec[pi[j]].pop_back() ;
}
else break ;
}
}
for(int j = Now , u ; j <= n ; j ++)
if(vis[pi[j]] != x) {
u = pi[j] ;
while(fa[u] && d[fa[u]] > x)
u = fa[u] ;
dfs(u , 0) ;
ans += min(fdis[u] > 0 ? f[u][1] + fdis[u] : INF , f[u][0]) ;
}
printf("%lld ",ans) ;
}
return 0 ;
}
CF1119F Niyaz and Small Degrees的更多相关文章
- CF1119F Niyaz and Small Degrees【treedp+堆】
如果枚举d来dp,那么就是设f[u][0/1]为u点不断/断掉和父亲的边,然后优先选取f[v][1]+w(u,v)<=f[v][0]的,如果断掉这些度数还是多就用一个堆维护剩下的按f[v][1] ...
- 树形DP ---- Codeforces Global Round 2 F. Niyaz and Small Degrees引发的一场血案
Aspirations:没有结果,没有成绩,acm是否有意义?它最大的意义就是让我培养快速理解和应用一个个未知知识点的能力. ————————————————————————————————————— ...
- 【codeforces contest 1119 F】Niyaz and Small Degrees
题目 描述 \(n\) 个点的树,每条边有一个边权: 对于一个 \(X\) ,求删去一些边后使得每个点的度数 \(d_i\) 均不超过 \(X\) 的最小代价: 你需要依次输出 \(X=0 \to n ...
- CF 1119F Niyaz and Small Degrees
打VP的时候由于CXR和XRY切题太快了导致我只能去写后面的题了 然而VP的时候大概还有一小时时想出了\(O(n^2\log n)\)的暴力,然后过了二十分钟才想到删点的优化 结果细节很多当然是写不出 ...
- [codeforces contest 1119 F] Niyaz and Small Degrees 解题报告 (树形DP+堆)
interlinkage: http://codeforces.com/contest/1119/problem/F description: 有一颗$n$个节点的树,每条边有一个边权 对于一个$x$ ...
- Solution -「CF 1119F」Niyaz and Small Degrees
\(\mathcal{Description}\) Link. 给定一棵 \(n\) 个结点的树,边有边权,对于每个整数 \(x\in[0,n)\),求出最少的删边代价使得任意结点度数不超过 ...
- Codeforces Global Round 2 Solution
这场题目设置有点问题啊,难度:Div.2 A->Div.2 B->Div.2 D->Div.2 C->Div.2 D->Div.1 D-> Div.1 E-> ...
- Codeforces Global Round 2 部分题解
F.Niyaz and Small Degrees 挺sb的一题,为什么比赛时只过了4个呢 考虑当\(x\)固定的时候怎么做.显然可以树形DP:设\(f_{u,i=0/1}\)表示只考虑\(u\)子树 ...
- [TimusOJ1057]Amount of Degrees
[TimusOJ1057]Amount of Degrees 试题描述 Create a code to determine the amount of integers, lying in the ...
随机推荐
- Effective Java Profiling With Open Source Tools
https://www.infoq.com/articles/java-profiling-with-open-source
- 【网络】TCP的拥塞控制
一.拥塞控制的一般原理 拥塞:对网络中某一资源的需求超过了该资源所能提供的可用部分 拥塞控制是防止过多的数据注入到网络,这样可以使网络中的路由器或链路不致过载,拥塞控制是一个全局性的过程. 流量控制往 ...
- [正在学习开发板]分享--- iTOP-4412移植CAN
首先拷贝迅为提供的 libcanjni.tar.gz 压缩包到 android 源代码的"iTop4412_ICS/device/samsung/common"文件夹以下,然后使用 ...
- Android 自己定义UI文章汇总
<Android ListView分类/分组效果 - 第一种实现方式> <Android ListView分类/分组效果 - 另外一种实现方式> <Android Lis ...
- win8系统 重装系统如何删除EFI分区
在PE下(一般重装系统就是在PE下),依次输入如下命令(注意虽然显示的是中文名"磁盘0",但是还是用英文disk 0) list disk select disk 0 clean
- udhcp源码详解(四) 之租赁IP的管理
Server端对于租赁出去的IP的管理是基于结构体dhcpOfferedAddr的,该结构体的定义是在leases.c文件里:(结构体的成员介绍说明见详解之数据结构) 1: struct dhcpOf ...
- C/C++ scanf 函数中%s 和%c 的简单差别
首先声明:在键盘中敲入字符后,字符会首先保存在键盘缓冲区中供scanf函数读取(scanf.getchar等函数是读取缓冲区,getch函数是读取的控制台信息,即为直接从键盘读取).另外特别注意键盘上 ...
- JAVA设计模式(01):创建型-工厂模式【工厂方法模式】(Factory Method)
简单工厂模式尽管简单,但存在一个非常严重的问题.当系统中须要引入新产品时,因为静态工厂方法通过所传入參数的不同来创建不同的产品,这必然要改动工厂类的源码,将违背"开闭原则".怎样实 ...
- is id() == 从内存的最小化占用角度解释 我是孕育者,我也应该这样设计 变,必然伴随着加法 一个list是否可以执行set()
def f(a, b): print(a is b, b is a, a == b, a.__eq__(b), id(a), id(b)) f(2, 2) f([2], [2]) f('2', '2' ...
- handsontable整理
hansontable简介 hansontable是一个在线类似Excel的表格编辑器,支持丰富的展现和交互,有多样的单元格类型供配置. 核心是由原生JavaScript构建,充分模块化,支持自定义b ...