• [BZOJ3631]:[JLOI2014]松鼠的新家(LCA+树上差分)


    题目传送门


    题目描述

    松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,……,最后到an,去参观新家。
    可是这样会导致维尼重复走很多房间,懒惰的维尼不听地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。维尼是个馋家伙,立马就答应了。
    现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

    输入格式

    第一行一个整数n,表示房间个数
    第二行n个整数,依次描述a1-an
    接下来n-1行,每行两个整数xy,表示标号xy的两个房间之间有树枝相连。

    输出格式

    一共n行,第i行输出标号为i的房间至少需要放多少个糖果,才能让维尼有糖果吃。

    样例

    样例输入:
    5
    1 4 5 3 2
    1 2
    2 4
    2 3
    4 5
    样例输出:
    1
    2
    1
    2
    1

    数据范围与提示

    对于所有数据,2≤n≤300000

    题解

    乍一看首先应该会想到熟树链剖分,但是认真思考之后会发现输出是在最后,那么我们可以考虑树上差分,经典操作。
    不会树上差分的大佬可以跳转这篇博客:树上差分
    具体解法在此就不做过赘述,注意第一个点的答案需要++即可。

    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    struct rec
    {
    	int nxt;
    	int to;
    }e[600001];
    int head[300001],depth[300001],fa[300001][21],cnt,sum,n;
    bool vis[300001];
    int a[300001],wzc[300001],miao[300001];
    void add(int x,int y)
    {
    	e[++cnt].nxt=head[x];
    	e[cnt].to=y;
    	head[x]=cnt;
    }
    void dfs(int x)
    {
        vis[x]=1;
        for(int i=head[x];i;i=e[i].nxt)
        {
            if(vis[e[i].to])continue;
            depth[e[i].to]=depth[x]+1;
            fa[e[i].to][0]=x;
            dfs(e[i].to);
        }
    }
    int lca(int x,int y)//倍增LCA
    {
        if(depth[x]<depth[y])swap(x,y);
        int k=depth[x]-depth[y]; 
        for(int i=0;i<20;i++) 
            if((1<<i)&k)x=fa[x][i]; 
        if(x==y)return x;
        for(int i=20;i>=0;i--) 
            if(fa[x][i]!=fa[y][i])
    		{ 
                x=fa[x][i];
    			y=fa[y][i]; 
            }
        return fa[x][0]; 
    }
    void _doudou(int x)
    {
    	vis[x]=1;
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		if(vis[e[i].to])continue;
    		_doudou(e[i].to);
    		miao[x]+=miao[e[i].to];
    	}
    	miao[x]+=wzc[x];
    }
    int main()
    {
    	int n;
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	for(int i=1;i<n;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		add(y,x);
    	}
        for(int i=1;i<=n;i++)//预处理过程
            if(!fa[a[i]][0])
    		{
                dfs(a[i]);
    			break;
            } 
        for(int i=1;i<=20;i++)
            for(int j=1;j<=n;j++)
                fa[j][i]=fa[fa[j][i-1]][i-1];//统计第几代父亲
        for(int i=2;i<=n;i++)
        {
        	int flag=lca(a[i-1],a[i]);
        	wzc[a[i]]++;
        	wzc[a[i-1]]++;
        	wzc[flag]--;
        	wzc[fa[flag][0]]--;//树上差分操作
        }
        memset(vis,0,sizeof(vis));
        _doudou(a[1]);//统计答案
        miao[a[1]]++;
        for(int i=1;i<=n;i++)printf("%d
    ",miao[i]-1);
    	return 0;
    }
    

     

    rp++

  • 相关阅读:
    一个随机数生成函数
    HTTP Post请求过程详解
    md5加密算法c语言版
    android popupwindow 自定义视图
    android 获取顶部状态栏的高度
    android 显示和隐藏输入框
    android tablayout + recycleview 简单使用
    jetpack paging使用
    android 自定义控件 属性配置
    vueLazyload 图片懒加载
  • 原文地址:https://www.cnblogs.com/wzc521/p/11044533.html
Copyright © 2020-2023  润新知