CF1119F Niyaz and Small Degrees 题解
题目描述
给定一棵 \(n\) 个点的树,边有边权。
\(\forall0\le k\le n-1\) ,求删掉的边的权值和最小值,使得每个点的度数 \(\le k\) 。
数据范围
- \(2\le n\le 2.5\cdot 10^5\) 。
- \(1\le u\neq v\le n,1\le w\le 10^6\) 。
时间限制 \(\texttt{3s}\) ,空间限制 \(\texttt{256MB}\) 。
分析
先考虑暴力的树形 \(\texttt{dp}\) 做法。固定 \(k\) ,记 \(dp_{u,0/1}\) 表示不删除 \(/\) 删除 \((u,fa_u)\) 这条边, \(u\) 子树的最小代价。
先不考虑 \(u\) 的度数限制,转移方程 \(dp_{u,*}=\sum\limits_{v\in son_u}\min(dp_{v,0},dp_{v,1}+w)\) 。
为了让 \(u\) 符合度数限制,对于 \(dp_{u,0},dp_{u,1}\) ,我们分别需要删 \(deg_u-k,deg_u-k-1\) 条边。
先全取 \(dp_{v,0}\) ,并且把 \(dp_{v,1}+w-dp_{v,0}\) 丢入小根堆中。
显然贪心取堆中的前若干小最优,注意 \(\lt 0\) 的值一定取。
算上外层枚举 \(k\) 的代价,时间复杂度 \(\mathcal O(n^2\log n)\) 。
我们并不关心 \(deg_u\le k\) 的点,只需对每个 \(deg_u\gt k\) 的点构成的连通块做树形 \(\texttt{dp}\) 。
这样时间复杂度有了显著优化:
对每个点 \(u\) 开一个 multiset ,存储所有的 \(x\) 满足可以通过 \(x\) 的代价让 \(deg_x\) 减少 \(1\) ,初始加入所有连向 \(deg\le k\) 的点的权值,
然后对 \(deg\gt k\) 的连通块 \(\texttt{dp}\) ,如果 \(dp_{v,0}<dp_{v,1}+w\) ,则将 \(dp_{v,1}+w-dp_{v,0}\) 插入 multiset 。
然后一直弹出最大值直到 multiset 大小等于 \(deg_u-k\) ,就可以得到 \(dp_{u,0}\) 的值,\(dp_{u,1}\)还需要多弹一个。
时间复杂度 \(\mathcal O(n\log n)\) 。
以下是代码实现细节。
从小到大扫描 \(k\) ,这样每次只需删除 \(deg=k\) 的点。扫描它的所有 \(deg\gt k\) 的邻点,并将边权加入
multiset。树形 \(\texttt{dp}\) 结束后,我们需要将
multiset还原,因此再开两个vector记录插入了哪些数和弹出了哪些数,dfs结束后再逆回去。为了避免复杂度退化,遍历出边时只能扫描 \(deg\gt k\) 的点,因此我们需要将邻点按
deg降序排序,扫到 \(deg\le k\) 的点时推出循环。细节有亿点点多。
#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=2.5e5+5;
int k,n,u,v,w,cur;
int deg[maxn],vis[maxn];
pii p[maxn];
vector<pii> g[maxn];
multiset<ll> s[maxn];
ll sum[maxn],dp[maxn][2];
bool cmp(pii a,pii b)
{
return deg[a.fi]>deg[b.fi];
}
#define s s[x]
void add(int x,ll v)
{
s.insert(v),sum[x]+=v;
}
void del(int x,ll v)
{
sum[x]-=v,s.erase(s.find(v));
}
#undef s
void dfs(int u)
{
vis[u]=k;
int cnt=deg[u]-k;
while(s[u].size()>cnt) del(u,*--s[u].end());
ll res=0;
vector<ll> a,b;
for(auto p:g[u])
{
int v=p.fi,w=p.se;
if(deg[v]<=k) break;
if(vis[v]==k) continue;
dfs(v);
res+=min(dp[v][0],dp[v][1]+w);
if(dp[v][0]<dp[v][1]+w)
{
ll x=dp[v][1]+w-dp[v][0];
b.push_back(x),add(u,x);
}
else cnt--;
}
while(s[u].size()>max(cnt,0))
{
auto x=*--s[u].end();
a.push_back(x),del(u,x);
}
dp[u][0]=res+sum[u];
while(s[u].size()>max(cnt-1,0))
{
auto x=*--s[u].end();
a.push_back(x),del(u,x);
}
dp[u][1]=res+sum[u];
for(auto p:a) add(u,p);
for(auto p:b) del(u,p);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n-1;i++)
{
scanf("%d%d%d",&u,&v,&w);
g[u].push_back(mp(v,w)),g[v].push_back(mp(u,w));
deg[u]++,deg[v]++;
}
for(int i=1;i<=n;i++)
{
vis[i]=-1,p[i]=mp(deg[i],i);
sort(g[i].begin(),g[i].end(),cmp);
}
sort(p+1,p+n+1);
for(k=0,cur=1;k<=n-1;k++)
{
while(cur<=n&&p[cur].fi<=k)
{
int u=p[cur++].se;
for(auto p:g[u])
{
if(deg[p.fi]<=k) break;
add(p.fi,p.se);
}
}
ll res=0;
for(int i=cur;i<=n;i++)
if(vis[p[i].se]!=k)
{
int u=p[i].se;
dfs(u),res+=dp[u][0];
}
printf("%lld ",res);
}
putchar('\n');
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16573823.html
浙公网安备 33010602011771号