hdu 6504 Split The Tree —— 主席树,对树复制粘贴

You are given a tree with n vertices, numbered from 1 to n. ith vertex has a value wi
We define the weight of a tree as the number of different vertex value in the tree.
If we delete one edge in the tree, the tree will split into two trees. The score is the sum of these two trees’ weights.
We want the know the maximal score we can get if we delete the edge optimally
Input
Input is given from Standard Input in the following format:
n
p2 p3 . . . pn
w1 w2 . . . wn
Constraints
2 ≤ n ≤ 100000
1 ≤ pi < i
1 ≤ wi ≤ 100000(1 ≤ i ≤ n), and they are integers
pi means there is a edge between pi and i
Output
Print one number denotes the maximal score
Sample Input
3
1 1
1 2 2
3
1 1
1 1 1
Sample Output
3
2

题意:

给你一棵树,每个点上有一个值,一棵树的值是这棵树上的不同权值点的数量。让你切掉一条边使得两棵树的值相加最大。

题解:

第一棵树的dfs序的最后一个连向第二颗树的根节点做dfs序的主席树,之后dfs枚举减掉每一条边的情况,剪掉的这个子树可能会把这棵树分成三份,但是第一份与第三份无法同时查找,那么这时候刚开始做的连相同的树就可以解决这个问题:
在这里插入图片描述
对于删除红叉叉的边,分成两棵树,也就是两个红色框起来的树,看到这个图应该明白怎么做了吧?

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int num[N*40],ls[N*40],rs[N*40],rt[N*40];
int tot,n;
int p[N],w[N];
struct node
{
    int to,next;
}e[N*2];
int head[N],cnt;
void add(int x,int y)
{
    e[cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt++;
}
int dfn[N],pre[N];
void build(int l,int r,int root)
{
    num[root]=0;
    if(l==r)
        return ;
    int mid=l+r>>1;
    build(l,mid,ls[root]=++tot);
    build(mid+1,r,rs[root]=++tot);
    //num[root]=num[ls[root]]+num[rs[root]];
}
void update(int l,int r,int root,int last,int pos,int val)
{
    ls[root]=ls[last];
    rs[root]=rs[last];
    num[root]=num[last]+val;
    if(l==r)
        return ;
    int mid=l+r>>1;
    if(mid>=pos)
        update(l,mid,ls[root]=++tot,ls[last],pos,val);
    else
        update(mid+1,r,rs[root]=++tot,rs[last],pos,val);
}
int query(int l,int r,int root,int ql,int qr)
{
    if(l>=ql&&r<=qr)
        return num[root];
    int mid=l+r>>1;
    int ans=0;
    if(mid>=ql)
        ans+=query(l,mid,ls[root],ql,qr);
    if(mid<qr)
        ans+=query(mid+1,r,rs[root],ql,qr);
    return ans;
}
int dfsnum,lef[N],rig[N],fin;
void dfs(int x)
{
    dfn[x]=++dfsnum;
    if(dfsnum==n)
        fin=x;
    if(pre[w[x]])
    {
        update(1,200005,rt[dfn[x]]=++tot,rt[dfn[x]-1],dfn[x],1);
        int ne=++tot;
        update(1,200005,ne,rt[dfn[x]],pre[w[x]],-1);
        rt[dfn[x]]=ne;
    }
    else
        update(1,200005,rt[dfn[x]]=++tot,rt[dfn[x]-1],dfn[x],1);
    pre[w[x]]=dfn[x];
    lef[x]=dfsnum;
    for(int i=head[x];~i;i=e[i].next)
        dfs(e[i].to);
    rig[x]=dfsnum;
}
int ans;
void del(int x)
{
    if(dfn[x]==n)
        return ;
    for(int i=head[x];~i;i=e[i].next)
    {
        int ne=e[i].to;
        int aa=query(1,200005,rt[rig[ne]],lef[ne],rig[ne]);
        ans=max(ans,aa+query(1,200005,rt[dfn[ne]-1+n],rig[ne]+1,dfn[ne]-1+n));
        del(ne);
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        memset(head,-1,sizeof(head));
        memset(pre,0,sizeof(pre));
        tot=cnt=ans=dfsnum=0;
        build(1,200005,rt[0]=++tot);
        for(int i=2;i<=n;i++)
            scanf("%d",&p[i]),add(p[i],i),add(p[i]+n,i+n);
        for(int i=1;i<=n;i++)
            scanf("%d",&w[i]),w[i+n]=w[i];
        //for(int i=head[2];~i;i=e[i].next)
            //cout<<e[i].to<<endl;
        dfs(1);
        add(fin,n+1);
        dfs(n+1);
        del(1);
        printf("%d\n",ans);
    }
    return 0;
}


/*
4
1 1 1
1 2 2 1
3
1 1
1 2 2
3
1 1
1 1 1
5
1 1 3 3
1 1 2 3 4
3
1 2
1 2 2
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值