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;
}

完结撒花~

posted @ 2025-11-13 12:04  FallingGardenia  阅读(21)  评论(0)    收藏  举报