2019雅礼集训 D8T3 union [主席树]

Posted p-b-p-b

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019雅礼集训 D8T3 union [主席树]相关的知识,希望对你有一定的参考价值。

题目描述:

技术分享图片

样例:

input:
12
1 1 1 4 1 1 5 2 8 1 6

output:
3
6
11
6
7
6
11
8
11
11
11
11

数据范围与约定:

技术分享图片


一道比较正常的主席树题

我才不会告诉你我考场上写了树链剖分+线段树+set还写挂了呢

容易发现我们只需要把删去每个节点后的联通块最大值(mx_1),次大值(mx_2)和最小值(mn)求出来,然后在(mx_1)里面抠下来一块大小为(v)的子树连到(mn?)里即可。

其中最优的(v_0?)满足(mx_1-v_0=v_0+mn?)

然而能不能刚好抠下来大小为(v)的一块还是个问题。

假设当前要求答案的点是(x?),对于不是(x?)的祖先的节点(y?),把它的父节点改一下就会使(size_y?)个点改变位置;而对于(x?)的某一个祖先(z?),改变的则是(size_z-size_x?)

所以我们需要分两种情况进行讨论。

建两棵主席树:第一棵建在(dfs)序上,以便快速统计子树信息;第二棵建在树上,以便快速统计链的信息。两棵主席树均是以(size)为关键字的值域线段树。

然后分类讨论:

(mx_1)(x)的某一个儿子的子树时:直接用第一棵主席树二分出最优的v即可。
(mx_1)是整棵树除去(x)的子树时:
1:在(x)(1)的链上:查询这一条链,注意加上和减去(size_x)
2:不在链上:用整棵树减去x的子树再减去(x)(1)的链,然后查询,注意加上和减去(size_x)

最后还要特判一下1只有一个儿子时输出(n-1?)

个人认为两棵不同的主席树相减还是非常骚的,第一次见到。

代码:

#include<bits/stdc++.h>
namespace my_std{
    using namespace std;
    #define mod 998244353
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define sz 100100
    typedef long long ll;
    template<typename T>
    inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();
        double d=0.1;
        while(ch>‘9‘||ch<‘0‘) f|=(ch==‘-‘),ch=getchar();
        while(ch<=‘9‘&&ch>=‘0‘) t=t*10+ch-48,ch=getchar();
        if(ch==‘.‘)
        {
            ch=getchar();
            while(ch<=‘9‘&&ch>=‘0‘) t+=d*(ch^48),d*=0.1,ch=getchar();
        }
        t=(f?-t:t);
    }
    template<typename T,typename... Args>
    inline void read(T& t,Args&... args){read(t); read(args...);}
    void file()
    {
        freopen("traffic.in","r",stdin);
        freopen("traffic.out","w",stdout);
    }
    inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n;

struct hh{int t,nxt;}edge[sz<<1];
int head[sz],ecnt;
void make_edge(int f,int t){edge[++ecnt]=(hh){t,head[f]};head[f]=ecnt;}

#define Tree sz*60

int sum[Tree],ls[Tree],rs[Tree],cnt;
int rt1[sz],rt2[sz];
void insert(int &k,int pre,int l,int r,int x)
{
    k=++cnt;sum[k]=sum[pre]+1;ls[k]=ls[pre],rs[k]=rs[pre];
    if (l==r) return;
    int mid=(l+r)>>1;
    if (x<=mid) insert(ls[k],ls[pre],l,mid,x);
    else insert(rs[k],rs[pre],mid+1,r,x);
}
#define lson ls[a],ls[b],ls[c],ls[d],l,mid
#define rson rs[a],rs[b],rs[c],rs[d],mid+1,r
int queryL(int a,int b,int c,int d,int l,int r,int x) 
{
    if (l>x) return -1;
    if (sum[a]+sum[b]-sum[c]-sum[d]==0) return -1;
    if (l==r) return l;
    int mid=(l+r)>>1;
    int ret=queryL(rson,x);
    return ret==-1?queryL(lson,x):ret;
}
int queryR(int a,int b,int c,int d,int l,int r,int x)
{
    if (r<x) return -1;
    if (sum[a]+sum[b]-sum[c]-sum[d]==0) return -1;
    if (l==r) return l;
    int mid=(l+r)>>1;
    int ret=queryR(lson,x);
    return ret==-1?queryR(rson,x):ret;
}
//queryL & queryR 查询与x最接近的size 
#undef lson
#undef rson
#undef Tree

#define v edge[i].t

int dfn[sz],low[sz],pre[sz],fa[sz],size[sz],T;
void dfs(int x)
{
    size[x]=1;pre[dfn[x]=++T]=x;
    go(x) dfs(v),size[x]+=size[v];
    low[x]=T; 
}
void build1(){rep(i,1,n) insert(rt1[i],rt1[i-1],1,n,size[pre[i]]);}
void build2(int x)
{
    insert(rt2[x],rt2[fa[x]],1,n,size[x]);
    go(x) build2(v);
}

inline int max(int x,int y,int z){return max({x,y,z});}

void solve(int x)
{
    int mn=n,mx1=0,mx2=0;
    go(x)
    {
        if (size[v]>=mx1) mx2=mx1,mx1=size[v];
        else mx2=max(mx2,size[v]);
        mn=min(mn,size[v]);
    }
    if (x!=1) 
    {
        int t=n-size[x];
        if (t>mx1) mx2=mx1,mx1=t;
        else mx2=max(mx2,t);
        mn=min(mn,t);
    }
    int val=(mx1-mn)/2;
    int ans=n;
    #define update(v) ans=min(ans,v)
    if (mx1==n-size[x]&&x!=1)
    {
        int v1=queryL(rt2[x],0,0,0,1,n,val+size[x]);
        int v2=queryR(rt2[x],0,0,0,1,n,val+size[x]);
        if (v1!=-1) v1-=size[x],update(max(mx2,mx1-v1,mn+v1));
        if (v2!=-1) v2-=size[x],update(max(mx2,mx1-v2,mn+v2));
        
        v1=queryL(rt1[n],rt1[dfn[x]],rt1[low[x]],rt2[x],1,n,val);
        v2=queryR(rt1[n],rt1[dfn[x]],rt1[low[x]],rt2[x],1,n,val);
        if (v1!=-1) update(max(mx2,mx1-v1,mn+v1));
        if (v2!=-1) update(max(mx2,mx1-v2,mn+v2));
    }
    go(x) if (size[v]==mx1) 
    {
        int v1=queryL(rt1[low[v]],0,rt1[dfn[v]-1],0,1,n,val);
        int v2=queryR(rt1[low[v]],0,rt1[dfn[v]-1],0,1,n,val);
        if (v1!=-1) update(max(mx2,mx1-v1,mn+v1));
        if (v2!=-1) update(max(mx2,mx1-v2,mn+v2));
    }
    #undef update
    if (x==1&&mn==mx1) ans=n-1;
    printf("%d
",ans);
}

#undef v

int main()
{
    file();
    read(n);
    rep(i,2,n) read(fa[i]),make_edge(fa[i],i);
    dfs(1);build1();build2(1);
    rep(i,1,n) solve(i);
}



以上是关于2019雅礼集训 D8T3 union [主席树]的主要内容,如果未能解决你的问题,请参考以下文章

2019雅礼集训 D7T2 subsequence [DP,平衡树]

「雅礼集训2018」树

「雅礼集训2018」树

2019牛客国庆集训派对day2 C.Just h-index(主席树)

loj6029「雅礼集训 2017 Day1」市场 线段树+均摊分析

2019牛客国庆集训派对day2 C Just h-index 二分答案+主席树