【洛谷】【lca+树上差分】P3258 [JLOI2014]松鼠的新家
【题目描述:】
松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n(2 ≤ n ≤ 300000)个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在”树“上。
松鼠想邀请****前来参观,并且还指定一份参观指南,他希望**能够按照他的指南顺序,先去a1,再去a2,......,最后到an,去参观新家。可是这样会导致**重复走很多房间,懒惰的**不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。
**是个馋家伙,立马就答应了。现在松鼠希望知道为了保证**有糖果吃,他需要在每一个房间各放至少多少个糖果。
因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当**在参观的最后到达餐厅时就不需要再拿糖果吃了。
【输入格式:】
第一行一个整数n,表示房间个数第二行n个整数,依次描述a1-an
接下来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
【算法分析:】
根据****走的路径记录每个点走过的次数,就是需要放的糖果数目
从点i走到点j, 可以看做:从i走到lca(i, j)再走到j是最短的路径。
而如何把i -> lca, j -> lca的点全部自增1呢?
可以用树上差分来*护,设定一个一*数组cha
对于从i到j的一条路径,
cha[i]++; cha[j]++; cha[lca(i, j)]--; cha[f[lca(i, j)][0]]--;//点i, j最近公共祖先的父节点
然后从根节点开始dfs这棵树,对cha数组做一遍前缀和
最后对所有既是起点也是重点的点的经过次数-1.
【代码:】
1 //P3258 [JLOI2014]松鼠的新家 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 using namespace std; 6 7 const int MAXN = 300000 + 1; 8 const int K = 29 + 1; 9 10 int n, a[MAXN], ans[MAXN]; 11 int cha[MAXN]; 12 int deep[MAXN], f[MAXN][K]; 13 int edge_num, head[MAXN]; 14 struct Edge { 15 int to, next; 16 }h[MAXN << 1]; 17 18 inline int read() { 19 int x = 0, f = 1; char ch = getchar(); 20 while(ch < '0' || ch > '9') { 21 if(ch == '-') f = -1; 22 ch = getchar(); 23 } 24 while(ch >= '0' && ch <= '9') 25 x = (x << 3) + (x << 1) + ch - 48, ch = getchar(); 26 return x * f; 27 } 28 29 inline void Add(int from, int to) { 30 h[++edge_num].to = to; 31 h[edge_num].next = head[from]; 32 head[from] = edge_num; 33 } 34 35 void build(int u) { 36 for(int i = head[u]; i != -1; i = h[i].next) { 37 if(!deep[h[i].to]) { 38 deep[h[i].to] = deep[u] + 1; 39 f[h[i].to][0] = u; 40 build(h[i].to); 41 } 42 } 43 } 44 45 inline void fill() { 46 for(int j = 1; j < K; ++j) 47 for(int i = 1; i <= n; ++i) 48 f[i][j] = f[f[i][j - 1]][j - 1]; 49 } 50 51 int lca(int a, int b) { 52 if(deep[a] > deep[b]) 53 swap(a, b); 54 for(int i = K - 1; i >= 0; --i) 55 if(deep[f[b][i]] >= deep[a]) b = f[b][i]; 56 if(a == b) return a; 57 for(int i = K - 1; i >= 0; --i) { 58 if(f[b][i] != f[a][i]) { 59 a = f[a][i], b = f[b][i]; 60 } 61 } 62 return f[b][0]; 63 } 64 65 void dfs(int u) { 66 for(int i = head[u]; i != -1; i = h[i].next) { 67 if(h[i].to == f[u][0]) continue; 68 dfs(h[i].to); 69 cha[u] += cha[h[i].to]; 70 } 71 } 72 73 int main() { 74 memset(head, -1, sizeof(head)); 75 n = read(); 76 for(int i = 1; i <= n; ++i) a[i] = read(); 77 for(int i = 1; i < n; ++i) { 78 int x = read(), y = read(); 79 Add(x, y); 80 Add(y, x); 81 } 82 deep[a[n]] = 1; 83 build(a[n]); 84 fill(); 85 for(int i = 2; i <= n; ++i) { 86 int com = lca(a[i], a[i - 1]); 87 ++cha[a[i]], ++cha[a[i - 1]]; 88 --cha[com], --cha[f[com][0]]; 89 } 90 dfs(a[n]); 91 for(int i = 2; i <= n; ++i) 92 --cha[a[i]]; 93 for(int i = 1; i <= n; ++i) 94 printf("%d\n", cha[i]); 95 }