[APIO2021] 封闭道路 T3简要题解
作者:互联网
感觉是整场考试中思维最简单的一题
考场写了\(O(n(\sqrt n + \log n))\),在luogu和uoj都是73,然后可能是因为没打.h,在CCF上爆零了,然后喜提铜牌
此处做法的复杂度是\(O(n\log n)\)的,在luogu上跑了440ms左右,还是挺快的
\(solution\)
-
首先我们考虑朴素\(dp\)怎么做,方便起见,以下我们都计算允许每个点保留\(k\)条边,最大的边权和,容易发现这个和答案刚好是互补的,最后用总数减以下即可
-
我们注意到每个\(k\)之间,\(dp\)是没有关联的,考虑\(dp1_u\),为\(u\)的子树内(注意这里不考虑与父亲节点的边是否选择),所有点都选了不超过\(k\)条边,\(u\)选了\(k\)条边的最优方案,\(dp2_u\)表示\(u\)的子树内,所有点都选了不超过\(k\)条边,\(u\)选了k-1条边的最优方案,这里可以理解为剩下的那条边要连到父亲
-
考虑如何转移,我们考虑贪心的转移,首先,假设我们一条边都不选,答案显然是\(\sum_{v \in son(u)}{dp1_v}\),接下来我们有k次反悔的机会,每次可以选一条边,那么贡献就是\(w + dp2_v - dp1_v\),我们设它为\(f_v\),那么\(dp1_u = max(\sum_{v \in son(u)}{dp1_v} + [至多k个f_v])\),\(dp2_u\)则是至多\(k-1\)个\(f_v\)
-
容易用排序做到\(O(n^2\log)\)
-
考虑单\(\log\)怎么做,我们不妨先分析点性质
-
定义:假设我们当前在计算允许保留\(k\)条边,一个点被定义为无用的,当且仅当\(degree(v)(以下简称deg_v) \leq k\)
-
首先我们注意到无用点和无用点之间的边我们无需决策,是一定可以选的,这部分贡献我们可以事先用差分来预处理
for(int u = 0; u < N; ++u)
for(auto v:E[u]){
int o = max(deg[u],deg[v]);
F[o] += w;
}
for(int i = 1; i < N; ++i) F[i] += F[i-1];
- 考虑有用点\(u\)和无用点\(v\)之间的决策,我们发现这部分决策是平凡的,因为如果一个点成为了无用点,那么在之后,其贡献将一直不变,保持为\(w(u,v)\),那么我们可以考虑用一个堆来维护,那么我们每次可以只计算有用点和有用点之间的贡献,不够的再直接用无用点的堆的前缀和来补充
- 此部分代码如下(注意此处无需考虑父子关系)
void _delete(int u){
for(auto now:E[u]){
int v = now.fi,w = now.se;
q[v].push(-w);Sw[v] += w;
}
}
- 那么我们可以发现,对于每个\(k\),我们只需保留\(deg \ge k\)的点形成的森林来遍历,我们可以先在邻接表上按度数排序,用vector的pop_back和双指针来删除点,这样做的复杂度看起来很暴力,但是实际上我们可以分析,一个点至多只会被遍历度数次,而所有的点的度数和是线性级别的,加上堆和排序的复杂度,总复杂度就是\(O(n \log n)\)
- 有一些细节,比如如何计算堆的前\(k\)大的和?
如果嫌麻烦,可以直接暴力权值线段树,反正空间1G,假设当前计算到\(k\),我们可以让堆只保留前\(k\)大的,剩下的先弹进另一个堆里(这里可以叫做垃圾堆),待更新到\(k+1\)时,再尝试从垃圾堆中拿出最大的来更新堆,这样的话,对于每个点,都有堆大小\(\leq\)度数,而根据我们之前的分析,总度数和是线性,那么加上堆的复杂度就是单log的 - 时间复杂度\(O(n\log n)\),空间复杂度\(O(n)\)
- 代码如下
#include<bits/stdc++.h>
std::vector<long long> minimum_closure_costs(int N, std::vector<int> U,
std::vector<int> V,
std::vector<int> W);
using namespace std;
int read(){
char c = getchar();
int x = 0;
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48,c = getchar();
return x;
}
const int _ = 1e5 + 7;
int deg[_];int n;
#define ll long long
#define mp make_pair
#define fi first
#define se second
#define pb push_back
typedef pair<int,int> pii;
vector<pii>E[_];
vector<ll>F;
vector<int>Ver;
bool cmp(pii a,pii b){
return deg[a.fi] > deg[b.fi];
}
bool Cmp(int a,int b){
return deg[a] < deg[b];
}
priority_queue<ll>q[_],bq[_];ll Sw[_];
int dfn[_],dfncnt;
void dfs1(int u,int fa){
for(auto now:E[u]){
int v = now.fi,w = now.se;
if(v ^ fa){
int o = max(deg[u],deg[v]);
F[o] += w;
// cout<<o<<" "<<w<<'\n';
dfs1(v,u);
}
}
}
ll dp1[_],dp2[_];
int st[_],top,bac[_],bcnt;
void _delete(int u){
for(auto now:E[u]){
int v = now.fi,w = now.se;
q[v].push(-w);Sw[v] += w;
}
}
void work(int u,int par,const int D){
dfn[u] = D;
dp1[u] = dp2[u] = 0;/*dp1[u]取D个,dp2[u]取D-1个*/
for(auto now:E[u]){
int v = now.fi;
if(v ^ par){
// cout<<u<<' '<<v<<"E"<<'\n';
work(v,u,D);
}
}
ll ans = 0;
top = 0;
for(auto now:E[u]){
int v = now.fi,w = now.se;
if(v ^ par){
ans += dp1[v];
if(w > (dp1[v] - dp2[v])){
st[++top] = w - (dp1[v] - dp2[v]);
}
}
}
sort(st + 1,st + top + 1);
reverse(st + 1,st + top + 1);
ll S1 = 0,S2 = Sw[u];
dp1[u] = Sw[u];dp2[u] = Sw[u];
// if(D == 2) cout<<Sw[u]<<'\n';
assert(q[u].size() <= (unsigned int)D);
if((int)q[u].size() == D) dp2[u] += q[u].top();
// cout<<u<<'\n';
bcnt = 0;
// cout<<u<<'\n';
for(int i = 1; i <= min(top,D); ++i){/*选i个有用点,D-i个无用点*/
S1 += st[i];
if((int)q[u].size() > D - i){
bac[++bcnt] = q[u].top();
S2 += bac[bcnt];
q[u].pop();
}
dp1[u] = max(dp1[u],S2 + S1);
if(i == D) continue;
ll V = S2 + S1;if((int)q[u].size() >= D - i){
V += q[u].top();
}
dp2[u] = max(dp2[u],V);
}
// cout<<u<<'\n';
while(bcnt) q[u].push(bac[bcnt]),bcnt--;
// if(D == 2 && u == 1) cout<<dp1[u]<<'\n';
dp1[u] += ans;dp2[u] += ans;
if(!par) F[D] += dp1[u];
}
//vector<int> U;vector<int> V;vector<int> W;
vector<ll> minimum_closure_costs(int N,vector<int> U,vector<int> V,vector<int> W) {
ll S = 0;
// int N = read();
n = N;
/*for(int i = 0; i < N - 1; ++i){
U.pb(0),V.pb(0),W.pb(0);
U[i] = read(),V[i] = read(),W[i] = read();
}*/
for(int i = 1; i < N; ++i){
int u = U[i-1] + 1,v = V[i-1] + 1,w = W[i-1];
// u--,v--;
deg[u]++,deg[v]++;
S += w;
E[u].pb(mp(v,w));E[v].pb(mp(u,w));
}
for(int i = 1; i <= N; ++i) F.pb(0);
for(int i = 1; i <= N; ++i) Ver.pb(i);
sort(Ver.begin(),Ver.end(),Cmp);Ver.pb(0);
dfs1(1,0);
for(int i = 1; i <= N; ++i) sort(E[i].begin(),E[i].end(),cmp);
for(int i = 1; i < N; ++i) F[i] += F[i-1];
int l = 0;
// for(int i = 0; i < N; ++i) cout<<Ver[i]<<' ';
// cout<<'\n';
for(int i = 1; i < N; ++i){
while(l <= N && deg[Ver[l]] <= i){
_delete(Ver[l]);
l++;
}
// cout<<i<<' '<<l<<'\n';
for(int ver = l; ver < N; ++ver){
int u = Ver[ver];
int k = E[u].size() - 1;
while(k >= 0 && deg[E[u][k].fi] <= i){
E[u].pop_back();
k--;
}
}
// puts("hxsb");
for(int ver = l; ver < N; ++ver){
int u = Ver[ver];
while((int)q[u].size() > i){
ll x = -q[u].top();
q[u].pop();Sw[u] -= x;
bq[u].push(x);
}
if((int)q[u].size() < i){
if((int)bq[u].size()){
ll x = bq[u].top();
bq[u].pop();q[u].push(-x);Sw[u] += x;
}
}
else{
if((int)bq[u].size()) {
ll x = bq[u].top();ll y = -q[u].top();
if(x > y){
q[u].pop();bq[u].pop();
Sw[u] -= y;Sw[u] += x;
q[u].push(-x);bq[u].push(y);
}
}
}
}
for(int ver = l; ver < N; ++ver){
int u = Ver[ver];
if(dfn[u] != i) work(u,0,i);
}
}
for(int i = 0; i < N; ++i) F[i] = S - F[i];
// for(int i = 0; i < N; ++i) cout<<F[i]<<' ';
return F;
}
/*int main(){
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
minimum_closure_costs();
return 0;
}*/
标签:dp1,int,题解,T3,++,vector,bq,APIO2021,deg 来源: https://www.cnblogs.com/y-dove/p/14824013.html