P11038 【MX-X3-T5】「RiOI-4」Countless J-Light Decomposition
简要题意
给定 \(n\) 个点的带权树,对每个点可选择 \(\leq k\) 条边清零边权,求最小化的以根节点为起点的所有链的边权和。
思路
令 \(dp_u\) 表示 \(u\) 子树内的最小边权和,首先会取到 \(\max \limits_{v \in son_u} dp_v\),然后贪心地删去前 \(k\) 大的 \(dp_v + cost(u,v)\),用第 \(k + 1\) 大的去更新 \(dp_u\)。
上述思路时间复杂度 \(\mathcal O(n^2 \log n)\),需要优化。
注意到如果 \(outdegree_u\) 如果 \(\leq k\),那么节点 \(u\) 的贡献可以被 \(\max \limits_{v \in son_u} dp_v\) 等效替代,故只需保留 \(outdegree > k\) 的节点即可。对这个性质建立虚树,\(outdegree > k\) 的节点是关键点,否则为非关键点。
非关键点的转移是显然的,只需取 \(dp_u = \max \limits_{v \in son_u} dp_v\)。
对于关键点,开一棵平衡树维护其儿子的贡献即可。
时间复杂度 \(\mathcal O(n \log n)\)。
直到写这篇题解的时候,我的代码还是最优解。
Code
#include<iostream>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
class FastIO
{
private:
static const int BUFFER_SIZE=1<<16;
char inBuffer[BUFFER_SIZE];
int inPos,inLength;
char outBuffer[BUFFER_SIZE];
int outPos;
void readBuffer()
{
inPos=0;
inLength=cin.read(inBuffer,BUFFER_SIZE).gcount();
}
char getChar()
{
if(inPos>=inLength)
{
readBuffer();
}
if(inLength==0) return EOF;
return inBuffer[inPos++];
}
void putChar(char c)
{
if(outPos>=BUFFER_SIZE)
{
flush();
}
outBuffer[outPos++]=c;
}
public:
FastIO():inPos(0),inLength(0),outPos(0)
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
~FastIO()
{
flush();
}
void flush()
{
if(outPos>0)
{
cout.write(outBuffer,outPos);
outPos=0;
}
}
void skipWhitespace()
{
char c;
while((c=getChar())!=EOF&&isspace(c));
if(c!=EOF) inPos--;
}
FastIO& operator>>(int& x)
{
skipWhitespace();
char c=getChar();
bool negative=false;
if(c=='-')
{
negative=true;
c=getChar();
}
x=0;
while(c>='0'&&c<='9')
{
x=x*10+(c-'0');
c=getChar();
}
if(negative) x=-x;
if(c!=EOF) inPos--;
return *this;
}
FastIO& operator>>(long long& x)
{
skipWhitespace();
char c=getChar();
bool negative=false;
if(c=='-')
{
negative=true;
c=getChar();
}
x=0;
while(c>='0'&&c<='9')
{
x=x*10+(c-'0');
c=getChar();
}
if(negative) x=-x;
if(c!=EOF) inPos--;
return *this;
}
FastIO& operator<<(int x)
{
if(x==0)
{
putChar('0');
return *this;
}
if(x<0)
{
putChar('-');
x=-x;
}
char buffer[20];
int len=0;
while(x>0)
{
buffer[len++]='0'+(x%10);
x/=10;
}
for(int i=len-1;i>=0;i--)
{
putChar(buffer[i]);
}
return *this;
}
FastIO& operator<<(long long x)
{
if(x==0)
{
putChar('0');
return *this;
}
if(x<0)
{
putChar('-');
x=-x;
}
char buffer[25];
int len=0;
while(x>0)
{
buffer[len++]='0'+(x%10);
x/=10;
}
for(int i=len-1;i>=0;i--)
{
putChar(buffer[i]);
}
return *this;
}
FastIO& operator<<(const string& s)
{
for(char c:s)
{
putChar(c);
}
return *this;
}
FastIO& operator<<(const char* s)
{
while(*s)
{
putChar(*s++);
}
return *this;
}
FastIO& operator<<(char c)
{
putChar(c);
return *this;
}
}fio;
const int N=2e5+5;
const int LogN=20;
struct AVL_Tree
{
struct Node
{
int lft,rgt,height,siz;
long long val;
Node(int _lft=0,int _rgt=0,int _height=0,int _siz=0,long long _val=0):
lft(_lft),rgt(_rgt),height(_height),siz(_siz),val(_val){}
}Tree[N*10];
int point=0,root[N];
int NewNode(long long val)
{
Tree[++point]=(Node){0,0,1,1,val};
return point;
}
int GetBalance(int k)
{
return Tree[Tree[k].lft].height-Tree[Tree[k].rgt].height;
}
void Pushup(int k)
{
Tree[k].siz=Tree[Tree[k].lft].siz+Tree[Tree[k].rgt].siz+1;
Tree[k].height=max(Tree[Tree[k].lft].height,Tree[Tree[k].rgt].height)+1;
}
int L(int k)
{
int rgt=Tree[k].rgt;
Tree[k].rgt=Tree[rgt].lft;
Tree[rgt].lft=k;
Pushup(k),Pushup(rgt);
return rgt;
}
int R(int k)
{
int lft=Tree[k].lft;
Tree[k].lft=Tree[lft].rgt;
Tree[lft].rgt=k;
Pushup(k),Pushup(lft);
return lft;
}
int Balance(int k)
{
Pushup(k);
int BalanceFactor=GetBalance(k);
if(BalanceFactor>1)
{
if(GetBalance(Tree[k].lft)<0)
Tree[k].lft=L(Tree[k].lft);
return R(k);
}
if(BalanceFactor<-1)
{
if(GetBalance(Tree[k].rgt)>0)
Tree[k].rgt=R(Tree[k].rgt);
return L(k);
}
return k;
}
int Insert(int k,long long val)
{
if(!k) return NewNode(val);
if(val<=Tree[k].val)
Tree[k].lft=Insert(Tree[k].lft,val);
else
Tree[k].rgt=Insert(Tree[k].rgt,val);
return Balance(k);
}
int FindMin(int k)
{
while(Tree[k].lft) k=Tree[k].lft;
return k;
}
int Delete(int k,long long val)
{
if(!k) return 0;
if(val<Tree[k].val)
Tree[k].lft=Delete(Tree[k].lft,val);
else if(val>Tree[k].val)
Tree[k].rgt=Delete(Tree[k].rgt,val);
else
{
if(!Tree[k].lft||!Tree[k].rgt)
return Tree[k].lft|Tree[k].rgt;
int MinNode=FindMin(Tree[k].rgt);
Tree[k].val=Tree[MinNode].val;
Tree[k].rgt=Delete(Tree[k].rgt,Tree[MinNode].val);
}
return Balance(k);
}
long long KthElement(int k,int rank)
{
if(rank<=Tree[Tree[k].lft].siz) return KthElement(Tree[k].lft,rank);
else if(rank==Tree[Tree[k].lft].siz+1) return Tree[k].val;
else return KthElement(Tree[k].rgt,rank-Tree[Tree[k].lft].siz-1);
}
}AVL;
int n,head[N],nxt[N<<1],to[N<<1],w[N<<1],cnt=0,a[N],node[N],stk[N],point=1;
int dep[N],siz[N],son[N],father[N][LogN],outdegree[N],dfn[N],top[N],idx=0;
long long dp[N];
bool vis[N];
vector<int>edge[N];
void Add(int u,int v,int cost)
{
to[++cnt]=v;
w[cnt]=cost;
nxt[cnt]=head[u];
head[u]=cnt;
}
void Dfs_Count(int u,int fa)
{
father[u][0]=fa,siz[u]=1,dep[u]=dep[fa]+1;
for(int i=1;i<LogN;i++)
father[u][i]=father[father[u][i-1]][i-1];
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==fa) continue;
Dfs_Count(v,u);
outdegree[u]++;
siz[u]+=siz[v];
a[v]=w[i];
AVL.root[u]=AVL.Insert(AVL.root[u],w[i]);
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void Dfs_Split(int u,int new_top)
{
if(!u) return;
dfn[u]=++idx,top[u]=new_top;
Dfs_Split(son[u],new_top);
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==father[u][0]||v==son[u]) continue;
Dfs_Split(v,v);
}
}
int LCA(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]]>=dep[top[v]])
u=father[top[u]][0];
else
v=father[top[v]][0];
}
return dep[u]>=dep[v]?v:u;
}
int Find(int v,int u)
{
for(int i=LogN-1;i>=0;i--)
if(father[v][i]&&dep[father[v][i]]>dep[u])
v=father[v][i];
return v;
}
void Build_Virtual_Tree(int maxx)
{
int tp=0;
while(point<=n&&outdegree[node[point]]<=maxx)
vis[node[point]]=true,point++;
if(point>n)
{
dp[1]=0;
return;
}
vector<int>import;
for(int i=point;i<=n;i++)
import.push_back(node[i]);
sort(import.begin(),import.end(),[](int &x,int &y){return dfn[x]<dfn[y];});
stk[++tp]=1;
edge[1].clear();
for(int u:import)
{
if(u==1) continue;
int lca=LCA(u,stk[tp]);
if(lca!=stk[tp])
{
while(tp&&dfn[lca]<dfn[stk[tp-1]])
edge[stk[tp-1]].push_back(stk[tp]),tp--;
if(lca!=stk[tp-1])
{
edge[lca].clear();
edge[lca].push_back(stk[tp]);
stk[tp]=lca;
}
else
edge[lca].push_back(stk[tp]),tp--;
}
edge[u].clear();
stk[++tp]=u;
}
for(int i=1;i<tp;i++)
edge[stk[i]].push_back(stk[i+1]);
}
void Dfs_Virtual(int u,int maxx)
{
dp[u]=0;
for(int v:edge[u])
Dfs_Virtual(v,maxx);
if(vis[u])
{
for(int v:edge[u])
dp[u]=max(dp[u],dp[v]);
return;
}
vector<int>tmp;
for(int v:edge[u])
{
int son_u=Find(v,u);
tmp.push_back(son_u);
dp[u]=max(dp[u],dp[v]);
AVL.root[u]=AVL.Delete(AVL.root[u],a[son_u]);
AVL.root[u]=AVL.Insert(AVL.root[u],dp[v]+a[son_u]);
}
dp[u]=max(dp[u],AVL.KthElement(AVL.root[u],outdegree[u]-maxx));
for(int i=0;i<edge[u].size();i++)
{
int v=edge[u][i],son_u=tmp[i];
AVL.root[u]=AVL.Insert(AVL.root[u],a[son_u]);
AVL.root[u]=AVL.Delete(AVL.root[u],dp[v]+a[son_u]);
}
}
int main()
{
fio>>n;
for(int i=1;i<n;i++)
{
int u,v,cost;
fio>>u>>v>>cost;
Add(u,v,cost),Add(v,u,cost);
}
Dfs_Count(1,0);
Dfs_Split(1,1);
for(int i=1;i<=n;i++)
node[i]=i;
sort(node+1,node+n+1,[](int &x,int &y){return outdegree[x]<outdegree[y];});
for(int i=0;i<n;i++)
{
Build_Virtual_Tree(i);
Dfs_Virtual(1,i);
fio<<dp[1]<<' ';
}
return 0;
}
完结撒花~
浙公网安备 33010602011771号