• JLOI2014 | 松鼠的新家(树上差分)


    题目描述

    松鼠的新家是一棵树,前几天刚刚装修了新家,新家有 (n) 个房间,并且有 (n-1) 根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。

    松鼠想邀请小熊前来参观,并且还指定一份参观指南,他希望小熊能够按照他的指南顺序,先去 (a_1) ,再去 (a_2)(cdots),最后到 (a_n),去参观新家。可是这样会导致重复走很多房间,懒惰的维尼不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。

    小熊是个馋家伙,立马就答应了。现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。

    因为松鼠参观指南上的最后一个房间 (a_n) 是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

    输入格式

    第一行一个正整数 (n),表示房间个数第二行 (n) 个正整数,依次描述 (a_1, a_2,cdots,a_n)

    接下来 (n-1) 行,每行两个正整数 (x,y),表示标号 (x)(y) 的两个房间之间有树枝相连。

    输出格式
    一共 (n) 行,第 (i) 行输出标号为 (i) 的房间至少需要放多少个糖果,才能让小熊有糖果吃。

    输入输出样例

    输入 #1

    5
    1 4 5 3 2
    1 2
    2 4
    2 3
    4 5

    输出 #1

    1
    2
    1
    2
    1

    说明/提示

    对于全部的数据,(2 le n le 3 imes 10^52≤n≤3×10)

    ————————————————————————————————

    首先考虑一下题意,输入的第二行给定了松鼠的访问节点的顺序,由于树上两点的路径唯一,所以全程路径已知。

    对于这种关心树上路径的问题,树链剖分可做,但这东西我很久很久没写过了,所以用了一个思路比较简单的树上差分(基于点)的方法。

    Link: 浅谈差分和树上差分算法的基本应用

    LCA 是用倍增实现的,常数有点大,最慢的一个点在 Luogu 上跑了 927ms

    具体细节请见代码的注释

    代码如下

    #include <bits/stdc++.h>
    #define MAXN 300007
    using namespace std;
    int n,cf[MAXN],dep[MAXN];
    int a[MAXN],ans[MAXN],fa[MAXN][20];
    vector<int> G[MAXN]; 
    inline int readint() { //快速读入
    	int w=0,X=0; char ch=0;
    	while (!isdigit(ch)) w|=ch=='-',ch=getchar();
    	while (isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    	return w?-X:X;
    }
    void dfs(int u,int f) {
    	fa[u][0]=f,dep[u]=dep[f]+1;
    	for (int i=0;i<(int)G[u].size();i++)
    		if (G[u][i]!=f) dfs(G[u][i],u);
    }
    int lca(int a,int b) {
    	if (dep[a]>dep[b]) swap(a,b); //先让 b 在下面 
            //首先跳到同一深度
    	int c=dep[b]-dep[a];
    	for (int j=0;(1<<j)<=c;j++)
    		if ((1<<j)&c) b=fa[b][j];
    	//一起往上跳
            if (a!=b) {
    		for (int j=19;j>=0;j--)
    			if (fa[a][j]!=fa[b][j]) a=fa[a][j],b=fa[b][j];
    		a=fa[a][0];
    	}
    	return a;
    }
    void stat(int u) { //用差分数组统计答案
    	ans[u]=cf[u];
    	for (int i=0;i<(int)G[u].size();i++) {
    		if(G[u][i]==fa[u][0]) continue;
    		stat(G[u][i]); 
    		ans[u]+=ans[G[u][i]];
    	}
    }
    int main() {
    	memset(cf,0,sizeof(cf)); //差分数组
    	memset(fa,0,sizeof(fa)); //fa[i][j] 表示从 i 往上跳 2^j 次到达的节点
    	memset(dep,0,sizeof(dep)); //节点的深度
    	memset(ans,0,sizeof(ans)); //答案数组
    	n=readint();
    	for (int i=1;i<=n;i++) a[i]=readint(); 
    	for (int i=1;i<n;i++) {
    		int x=readint(),y=readint();
    		G[x].push_back(y),G[y].push_back(x);
    	}
            //倍增求 LCA 的预处理
    	dfs(1,0);
    	for (int j=1;j<20;j++)
    		for (int i=1;i<=n;i++)
    			fa[i][j]=fa[fa[i][j-1]][j-1];
    	for (int i=1;i<n;i++) {
    		cf[a[i]]++,cf[a[i+1]]++;
    		cf[lca(a[i],a[i+1])]--,cf[fa[lca(a[i],a[i+1])][0]]--; //查封操作		
    	}
    	stat(1);
    	for (int i=1;i<=n;i++) printf("%d
    ",a[1]==i?ans[i]:ans[i]-1); 
    	return 0;
    }
    
  • 相关阅读:
    egrep 正则邮箱
    centos 安装编译时常见错误总结
    线上系统和冷备系统同步单个表数据
    nagios微信报警配置
    saltstack批量加用户脚本
    阿里云服务器迁移流程
    HDU 4912 LCA + 贪心
    HDU 5242 树链剖分思想的贪心
    洛谷P3328(bzoj 4085)毒瘤线段树
    Codeforces 719E (线段树教做人系列) 线段树维护矩阵
  • 原文地址:https://www.cnblogs.com/zhwer/p/13167611.html
Copyright © 2020-2023  润新知