【CF768G】The Winds of Winter 可持久化线段树 DFS序

题目大意

  给定一棵 n 个点的树,对于树上每个结点,将它删去,然后可以将得到的森林中任意一个点与其父亲断开并连接到另一颗树上,对每一个点求出森林中所有树size最大值的最小值。

   n100000

题解

  首先用DFS序+可持久化线段树求出删掉这个点后剩下的联通块的大小的最大值 max 、次大值 sec 、最小值 min 。这里要维护两棵可持久化线段树,一棵是DFS序前缀的,一棵是从根到每个点的。

  那么肯定是在最大的连通块上切下一块接到最小的连通块上。

  假设切下的大小为 x ,那么答案是max(maxx,min+x,sec)。这个的图像是带一个向下的尖角的,这个尖角的位置为 max+min2 。所以我们要切下来的 x 就是maxmin2。我们只需要在对应的可持久化线段树上找这个值的前驱和后继并统计答案。

  切下来的部分有三种可能:

   1.在 x 的子树内,可以直接统计答案

   2.在x的子树外且不包含 x 到根的点,可以直接统计答案

   3.在x的子树外切包含根到 x 的点,查询到的子树大小要减掉sizex

  时间复杂度: O(nlogn)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<list>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
namespace sgt
{
    int rt1[100010];
    int rt2[100010];
    struct node
    {
        int lc,rc;
        int s;
        node()
        {
            lc=rc=s=0;
        }
    };
    node a[10000010];
    int cnt=0;
    int insert(int p1,int x,int l,int r)
    {
        int p=++cnt;
        a[p]=a[p1];
        a[p].s++;
        if(l==r)
            return p;
        int mid=(l+r)>>1;
        if(x<=mid)
            a[p].lc=insert(a[p].lc,x,l,mid);
        else
            a[p].rc=insert(a[p].rc,x,mid+1,r);
        return p;
    }
    int suf(int p1,int p2,int p3,int p4,int x,int l,int r)//p1+p3-p2-p4
    {
        int s=a[p1].s+a[p3].s-a[p4].s-a[p2].s;
        if(!s)
            return 0x3fffffff;
        if(l==r)
            return l;
        int mid=(l+r)>>1;
        int ls=a[a[p1].lc].s+a[a[p3].lc].s-a[a[p4].lc].s-a[a[p2].lc].s;
        if(x<=mid&&ls)
        {
            int lans=suf(a[p1].lc,a[p2].lc,a[p3].lc,a[p4].lc,x,l,mid);
            if(lans!=0x3fffffff)
                return lans;
        }
        return suf(a[p1].rc,a[p2].rc,a[p3].rc,a[p4].rc,x,mid+1,r);
    }
    int pre(int p1,int p2,int p3,int p4,int x,int l,int r)
    {
        int s=a[p1].s+a[p3].s-a[p4].s-a[p2].s;
        if(!s)
            return 0;
        if(l==r)
            return l;
        int mid=(l+r)>>1;
        int rs=a[a[p1].rc].s+a[a[p3].rc].s-a[a[p4].rc].s-a[a[p2].rc].s;
        if(x>mid&&rs)
        {
            int rans=pre(a[p1].rc,a[p2].rc,a[p3].rc,a[p4].rc,x,mid+1,r);
            if(rans)
                return rans;
        }
        return pre(a[p1].lc,a[p2].lc,a[p3].lc,a[p4].lc,x,l,mid);
    }
    int getmax(int p1,int p2,int p3,int p4,int l,int r)
    {
        int s=a[p1].s+a[p3].s-a[p4].s-a[p2].s;
        if(!s)
            return 0;
        if(l==r)
            return l;
        int mid=(l+r)>>1;
        int rs=a[a[p1].rc].s+a[a[p3].rc].s-a[a[p4].rc].s-a[a[p2].rc].s;
        if(rs)
            return getmax(a[p1].rc,a[p2].rc,a[p3].rc,a[p4].rc,mid+1,r);
        return getmax(a[p1].lc,a[p2].lc,a[p3].lc,a[p4].lc,l,mid);
    }
    int getmin(int p1,int p2,int p3,int p4,int l,int r)
    {
        int s=a[p1].s+a[p3].s-a[p4].s-a[p2].s;
        if(!s)
            return 0x3fffffff;
        if(l==r)
            return l;
        int mid=(l+r)>>1;
        int ls=a[a[p1].lc].s+a[a[p3].lc].s-a[a[p4].lc].s-a[a[p2].lc].s;
        if(ls)
            return getmin(a[p1].lc,a[p2].lc,a[p3].lc,a[p4].lc,l,mid);           
        return getmin(a[p1].rc,a[p2].rc,a[p3].rc,a[p4].rc,mid+1,r);
    }
}
using sgt::rt1;
using sgt::rt2;
using sgt::insert;
using sgt::suf;
using sgt::pre;
using sgt::getmax;
using sgt::getmin;
list<int> l[100010];
int f[100010];
int st[100010];
int ed[100010];
int s[100010];
int w[100010];
int ti;
int n;
void dfs1(int x)
{
    st[x]=++ti;
    w[ti]=x;
    s[x]=1;
    for(auto v:l[x])
    {
        dfs1(v);
        s[x]+=s[v];
    }
    ed[x]=ti;
}
int update(int &a,int &b,int &c)
{
    if(c>=a)
    {
        b=a;
        a=c;
        return 1;
    }
    else
    {
        b=max(b,c);
        return 2;
    }
    return 0;
}
int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    scanf("%d",&n);
    int rt,x,y;
    int i;
    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&y);
        if(x)
        {
            l[x].push_back(y);
            f[y]=x;
        }
        else
            rt=y;
    }
    dfs1(rt);
    for(i=1;i<=n;i++)
    {
        x=w[i];
        rt2[x]=insert(rt2[f[x]],s[x],1,n);
        rt1[i]=insert(rt1[i-1],s[x],1,n);
    }
    for(i=1;i<=n;i++)
    {
        int mx=0,sec=0,mi=0x7fffffff;
        int s1,s2,s3,s4,ans;
        int mv;

        for(auto v:l[i])
        {
            s1=s[v];
            if(update(mx,sec,s1)==1)
            {
                s4=1;
                mv=v;
            }
            mi=min(mi,s1);
        }
        s1=n-s[i];
        if(s1)
        {
            if(update(mx,sec,s1)==1)
                s4=2;
            mi=min(mi,s1);
        }
        ans=0x7fffffff;
        int mid=(mi+mx+1)>>1;
        int s5=mx-mid;
        if(i==1)
            int xxx=1;
        ans=min(ans,mx);
        if(s4==1)
        {
            s1=pre(rt1[ed[mv]],rt1[st[mv]-1],0,0,s5,1,n);
            s2=suf(rt1[ed[mv]],rt1[st[mv]-1],0,0,s5,1,n);
            ans=min(ans,max(sec,max(mi+s1,mx-s1)));
            ans=min(ans,max(sec,max(mi+s2,mx-s2)));
        }
        else if(s4==2)
        {
            s1=pre(rt2[f[i]],0,0,0,s5+s[i],1,n);
            s2=suf(rt2[f[i]],0,0,0,s5+s[i],1,n);
            if(s1)
                s1-=s[i];
            if(s2!=0x3fffffff)
                s2-=s[i];
            ans=min(ans,max(sec,max(mi+s1,mx-s1)));
            ans=min(ans,max(sec,max(mi+s2,mx-s2)));
            s1=pre(rt1[st[i]-1],rt1[ed[i]],rt1[n],rt2[f[i]],s5,1,n);
            s2=suf(rt1[st[i]-1],rt1[ed[i]],rt1[n],rt2[f[i]],s5,1,n);
            ans=min(ans,max(sec,max(mi+s1,mx-s1)));
            ans=min(ans,max(sec,max(mi+s2,mx-s2)));
        }
        printf("%d\n",ans);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值