专栏文章

题解:P13954 [ICPC 2023 Nanjing R] 红黑树

P13954题解参与者 3已保存评论 2

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@mio070rl
此快照首次捕获于
2025/12/02 11:13
3 个月前
此快照最后确认于
2025/12/02 11:13
3 个月前
查看原文
考虑朴素的 dp。不妨记 fi,jf_{i,j} 表示以 ii 为根节点的子树内,到所有的叶子的黑色点个数为 jj 最少需要选择多少个节点。转移是显然的,这样和树形背包复杂度是基本相同的,复杂度为 O(n2)O(n^2),在随机情况下复杂度为 O(nlogn)O(n\log n)
然后考虑优化。观察能得出对于每个 ii,把所有点 (j,fi,j)(j,f_{i,j}) 画到坐标系上,发现这是一个凸函数。感性理解是简单的,具体证明可以直接归纳,是不难的。然后就可以直接闵可夫斯基和了。
但是又不想写闵可夫斯基和,这时候发现这个凸包在第一维是连续的。所以可以考虑用 slope trick 来维护转移。所以对每个点的答案就是前缀的一段负数斜率加上 fi,0f_{i,0}。然后考虑根节点颜色的影响,实际上相当于在其中插入一个 1/11/-1 的斜率,这个很好理解,就是改变根节点的颜色会让黑色点个数 +/1+/-1,同时操作数也会变化 11,体现到斜率上就是 1/11/-1 的斜率。
所以可以直接用堆维护斜率,或者用两个数组分别维护正的斜率和负的斜率,再用一个变量维护 00 斜率的个数。时间复杂度取决于实现为单 log\log 或线性。
CPP
#include<bits/stdc++.h>
using namespace std;
#define __MY_TEST__ 0
inline int read()
{
    int t;
    cin>>t;
    return t;
}
const int N=1e5+5;
char col[N];
int sze[N],f[N];
vector<int>gra[N];
priority_queue<int,vector<int>,greater<int> >q[N];
void dfs(int u)
{
    for(auto v:gra[u])
    {
        dfs(v);
        sze[u]+=sze[v];
        if(q[u].empty()) swap(q[u],q[v]),f[u]=f[v];
        else
        {
            priority_queue<int,vector<int>,greater<int> >ts;
            f[u]=0;
            while(!q[u].empty()&&!q[v].empty())
            {
                int tmp=q[u].top()+q[v].top();
                q[u].pop(),q[v].pop();
                if(tmp<0) f[u]+=tmp;
                ts.push(tmp);
            }
            q[u]=ts;
        }
    }
    if(col[u]=='0') q[u].push(1);
    else q[u].push(-1),sze[u]++,f[u]--;
}
void work()
{
    int n=read();
    for(int i=1;i<=n;i++) gra[i].clear(),sze[i]=f[i]=0;
    for(int i=1;i<=n;i++) while(!q[i].empty()) q[i].pop();
    cin>>col+1;
    for(int i=2;i<=n;i++) gra[read()].push_back(i);
    dfs(1);
    for(int i=1;i<=n;i++)
    {
        cout<<sze[i]+f[i];
        if(i<n) cout<<' ';
    }
    cout<<'\n';
}
signed main(){
#if __MY_TEST__
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#endif
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int t=read();
    while(t--) work();
}

评论

2 条评论,欢迎与作者交流。

正在加载评论...