【题目】
CF
给定一棵
n
n
n个点带边权的树,对于所有
k
∈
[
0
,
n
−
1
]
k\in [0,n-1]
k∈[0,n−1],问最少删除边权和为多少的边,可以使得所有点度数不超过
k
k
k。
n
≤
2.5
×
1
0
5
,
c
≤
1
0
6
n\leq 2.5\times 10^5,c\leq 10^6
n≤2.5×105,c≤106
【解题思路】
不妨考虑对于一个
x
x
x怎么做。设
f
x
,
0
/
1
f_{x,0/1}
fx,0/1表示
x
x
x子树内,
x
x
x与它的父边断或不断的最小代价,这个可以简单转移就可以做到单次
O
(
n
)
O(n)
O(n)了。
考虑优化,一个可行的角度是:设 v v v为 x x x的一个儿子,若 f v , 1 + c ≤ f v , 0 f_{v,1}+c\leq f_{v,0} fv,1+c≤fv,0,那么一定会选择断开,因为这样一定会让 x x x的度数减少 1 1 1,这个就直接贡献了。否则假设这条边不断,最后可能会发现 x x x的度数不满足要求,那么实际上是要将若干个 f v , 0 f_{v,0} fv,0变成 f v , 1 + c f_{v,1}+c fv,1+c。
于是可以对每个点维护一个 f v , 1 + c − f v , 0 f_{v,1}+c-f_{v,0} fv,1+c−fv,0的堆,取若干个最小值即可。
接下来我们从小到大考虑每个
k
k
k,若一个点本身度数小于等于
k
k
k,那么某种意义上本来就可以不考虑它。具体来说哦我们可以将以这个点为端点的边丢到对应另一个端点的堆里,表示可以通过断开这条边使得度数
−
1
-1
−1。
接下来再对所有度数
>
k
>k
>k的点做上面的
DP
\text{DP}
DP即可。
复杂度是所有端点度数和乘上堆的 log \log log,也就是 O ( n log n ) O(n\log n) O(nlogn)
其实这个题还可以直接 DSU on Tree \text{DSU on Tree} DSU on Tree做到 O ( n log 2 n ) O(n\log ^2 n) O(nlog2n)
【参考代码】
#include<bits/stdc++.h>
#define fi first
#define se second
#define mkp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=250005;
int n,D,vis[N],du[N],nex[N],st[N];
ll ans,sum[N],f[N][2];
vector<pii>G[N];
vector<int>vec[N];
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(ll x){if(x>9)write(x/10);putchar(x%10^48);}
void writesp(ll x){write(x);putchar(' ');}
struct Heap
{
priority_queue<ll>q,p;
void push(ll x){q.push(x);}
void erase(ll x){p.push(x);}
int top(){while(!q.empty() && !p.empty() && q.top()==p.top())q.pop(),p.pop();return q.top();}
void pop(){top();q.pop();}
int size(){return q.size()-p.size();}
bool empty(){return q.size()==p.size();}
}q[N];
struct Tway{int v,w,nex;}e[N<<1];
void add(int u,int v,int w){G[u].pb(mkp(v,w));G[v].pb(mkp(u,w));}
bool cmp(const pii&A,const pii&B){return du[A.fi]<du[B.fi];}
void dfs(int x,int ff)
{
vis[x]=1;int res=du[x]-D;
while(q[x].size()>res) sum[x]-=q[x].top(),q[x].pop();
vector<ll>add,del;
ll s=0;
while(st[x]<(int)G[x].size() && du[G[x][st[x]].fi]<=D) ++st[x];
for(int i=st[x];i<(int)G[x].size();++i)
{
int v=G[x][i].fi,w=G[x][i].se;
if(vis[v] || v==ff) continue; dfs(v,x);
if(f[v][1]+w<=f[v][0]) --res,s+=f[v][1]+w;
else
{
s+=f[v][0];ll tmp=f[v][1]+w-f[v][0];
q[x].push(tmp);sum[x]+=tmp;del.pb(tmp);
}
}
while(q[x].size()>max(0,res)) sum[x]-=q[x].top(),add.pb(q[x].top()),q[x].pop();
f[x][0]=s+sum[x];
--res;while(q[x].size()>max(0,res)) sum[x]-=q[x].top(),add.pb(q[x].top()),q[x].pop();
f[x][1]=s+sum[x];
for(auto v:add) q[x].push(v),sum[x]+=v;
for(auto v:del) q[x].erase(v),sum[x]-=v;
}
void update(int x)
{
vis[x]=1;
for(auto v:G[x]) if(!vis[v.fi]) q[v.fi].push(v.se),sum[v.fi]+=v.se;
}
void init()
{
n=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read(),w=read();
add(u,v,w);++du[u];++du[v];ans+=w;
}
for(int i=1;i<=n;++i)
{
vec[du[i]].pb(i);
sort(G[i].begin(),G[i].end(),cmp);
}
nex[n]=n+1;
for(int i=n-1;i;--i)
{
if(vec[i+1].size()) nex[i]=i+1;
else nex[i]=nex[i+1];
}
}
void solve()
{
writesp(ans);
for(int i=1;i<n;++i)
{
for(auto v:vec[i]) update(v);
ans=0;D=i;
for(int j=i+1;j<=n;j=nex[j]) for(auto v:vec[j])
if(!vis[v]) dfs(v,0),ans+=f[v][0];
for(int j=i+1;j<=n;j=nex[j]) for(auto v:vec[j]) vis[v]=0;
writesp(ans);
}
}
int main()
{
#ifdef Durant_Lee
freopen("CF1119F.in","r",stdin);
freopen("CF1119F.out","w",stdout);
#endif
init();solve();
return 0;
}