P11038 【MX-X3-T5】「RiOI-4」Countless J-Light Decomposition
我们可以发现,对于一个点 \(u\),如果当前是 \(i\) 重且 \(u\) 的孩子个数比 \(i\) 少那么我们可以认为这个点不会对答案产生贡献。这给了我们思路。我们可以考虑对于当前枚举孩子个数,对于仍需要考虑哪些选的建虚树。由于一个点最多会被遍历孩子个数次,那么关键点总和还是 \(n\) 的。
我们只需要考虑关键点如何动规。考虑设 \(f_u\) 表示当前所有到 \(u\) 的最大贡献。那么对于不是关键点的,显然有 \(f_{u}=\max_v f_v\)。对于是关键点的,我们可以按 \(f_v+E(u,v)\) 排序,那么更新为:
\[f_u=\max(\max_v f_v,f_{v_{i+1}}+E(u,v_{i+1}))
\]
但是我们发现,虽然关键点总和是对的,但是上面那个 \(v\) 仍要从最开始的树进行访问。但是发现,只有存在关键点的子树 \(f_v\) 才不为 \(0\)。所以我们可以考虑先提前用数据结构存上所有的 \(E\),之后对于每一颗虚树的新关键点,我们删掉对应的 \(E\) 然后插入新的 \(f+E\) 即可。
我们需要一个能够查第 \(k\) 大,可随机删随机插入的数据结构。当然可以手写平衡树或者权值线段树。但是这里我们可以直接使用对顶 multiset 来存。我们开两个 multiset,其中一个存前 \(k\) 大的数,另一个存剩下的数。那么我们只需要写一个平衡函数来平衡两个之间的大小关系即可,插入就可以先插入到大 multiset,然后平衡。删除就找这个元素在哪里,删掉之后平衡即可。这样会比平衡树好写一些。
时间复杂度 \(O(n\log n)\)。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
typedef long long ll;
const int MAXN=2.5e5+5;
int n;
vector<pair<int,int>>adj[MAXN];
ll mi[MAXN][20];
int lg[MAXN];
int fa[MAXN][20],st[MAXN][20],dfn[MAXN],dfntot,dep[MAXN],sn[MAXN];
ll sm[MAXN];
multiset<ll>sa[MAXN],sb[MAXN];
int up=1;
void insert(int id,ll nw){
sa[id].insert(nw);
while(sa[id].size()>up){
sb[id].insert(*sa[id].begin());
sa[id].erase(sa[id].begin());
}
}
void erase(int id,ll x){
if(sa[id].count(x)){
sa[id].erase(sa[id].find(x));
}else if(sb[id].count(x)){
sb[id].erase(sb[id].find(x));
}
while(sa[id].size()<up&&!sb[id].empty()){
auto it=sb[id].end();
it--;
sa[id].insert(*it);
sb[id].erase(it);
}
}
void init(int u,int f){
dep[u]=dep[f]+1;
dfn[u]=++dfntot;
st[dfntot][0]=f;
fa[u][0]=f;
rep(i,1,19){
fa[u][i]=fa[fa[u][i-1]][i-1];
}
for(auto eg:adj[u]){
int v=eg.first,w=eg.second;
if(v==f){
continue;
}
sn[u]++;
sm[v]=sm[u]+w;
insert(u,w);
init(v,u);
}
}
int gno(int u,int v){
return dep[u]<dep[v]?u:v;
}
int lca(int u,int v){
if(u==v){
return u;
}
u=dfn[u],v=dfn[v];
if(u>v){
swap(u,v);
}
u++;
int j=lg[v-u+1];
return gno(st[u][j],st[v-(1<<j)+1][j]);
}
vector<pair<int,ll>>e[MAXN];
bool cmp(int u,int v){
return dfn[u]<dfn[v];
}
bool gd[MAXN];
void build(vector<int>s){
s.push_back(1);
sort(s.begin(),s.end(),cmp);
s.erase(unique(s.begin(),s.end()),s.end());
int nm=int(s.size())-2;
rep(i,0,nm){
s.push_back(lca(s[i],s[i+1]));
}
sort(s.begin(),s.end(),cmp);
s.erase(unique(s.begin(),s.end()),s.end());
for(auto v:s){
e[v].clear();
}
rep(i,0,int(s.size())-2){
int l=lca(s[i],s[i+1]);
int val=dep[s[i+1]]-dep[l]-1,nxt=s[i+1];
for(int j=0;val;++j){
if(val&1){
nxt=fa[nxt][j];
}
val>>=1;
}
e[l].push_back({s[i+1],sm[nxt]-sm[l]});
}
}
ll f[MAXN];
void dfs(int u){
f[u]=0;
for(auto eg:e[u]){
dfs(eg.first);
erase(u,eg.second);
insert(u,eg.second+f[eg.first]);
f[u]=max(f[u],f[eg.first]);
}
while(sa[u].size()<up&&!sb[u].empty()){
auto it=sb[u].end();
it--;
sa[u].insert(*it);
sb[u].erase(it);
}
if(sa[u].size()>=up){
f[u]=max(f[u],(*sa[u].begin()));
}
for(auto eg:e[u]){
erase(u,eg.second+f[eg.first]);
insert(u,eg.second);
}
}
int nm[MAXN];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
rep(i,2,n){
lg[i]=lg[i>>1]+1;
}
rep(i,1,n-1){
int u,v,w;
cin>>u>>v>>w;
adj[u].push_back({v,w});
adj[v].push_back({u,w});
}
init(1,0);
rep(j,1,lg[n]){
rep(i,1,n-(1<<j)+1){
st[i][j]=gno(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
rep(i,1,n){
nm[i]=i;
}
sort(nm+1,nm+n+1,[](int x,int y){
return sn[x]<sn[y];
});
int j=1;
rep(i,0,n-1){
while(j<=n&&sn[nm[j]]<=i){
++j;
}
if(j==n+1){
cout<<0<<" \n"[i==n];
continue;
}
vector<int>s;
rep(vv,j,n){
s.push_back(nm[vv]);
gd[nm[vv]]=true;
}
build(s);
up=i+1;
dfs(1);
cout<<f[1]<<" \n"[i==n];
rep(vv,j,n){
gd[nm[vv]]=false;
}
}
return 0;
}

浙公网安备 33010602011771号