LCA+树的直径,ACM暑期集训

                                   A-Bob’s Race

Problem - 4123 (hdu.edu.cn)

Problem Description

Bob wants to hold a race to encourage people to do sports. He has got trouble in choosing the route. There are N houses and N - 1 roads in his village. Each road connects two houses, and all houses are connected together. To make the race more interesting, he requires that every participant must start from a different house and run AS FAR AS POSSIBLE without passing a road more than once. The distance difference between the one who runs the longest distance and the one who runs the shortest distance is called “race difference” by Bob. Bob does not want the “race difference”to be more than Q. The houses are numbered from 1 to N. Bob wants that the No. of all starting house must be consecutive. He is now asking you for help. He wants to know the maximum number of starting houses he can choose, by other words, the maximum number of people who can take part in his race.

Input

There are several test cases.
The first line of each test case contains two integers N and M. N is the number of houses, M is the number of queries.
The following N-1 lines, each contains three integers, x, y and z, indicating that there is a road of length z connecting house x and house y.
The following M lines are the queries. Each line contains an integer Q, asking that at most how many people can take part in Bob’s race according to the above mentioned rules and under the condition that the“race difference”is no more than Q.

The input ends with N = 0 and M = 0.

(N<=50000 M<=500 1<=x,y<=N 0<=z<=5000 Q<=10000000)

Output

For each test case, you should output the answer in a line for each query.

Sample Input

5 5

1 2 3

2 3 4

4 5 3

3 4 2

1

2

3

4

5

0 0

Sample Output

1

3

3

3

5

解析:st表查询,树的直径,尺取,log2函数

先求出树上各点到直径两个端点的距离,然后用尺取找出连续的一段树节点是他们的能走的最大距离和最小距离的差不大于Q,其中区间内最大距离和最小距离查询需要用到st表,使用st表时千万不要用log2函数,不然会爆时间


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 5e4+ 5;
int n, m;
vector<pair<int, int>>G[N];
int d[N],val[N],dp[N][25], dp1[N][25],d1[N];
int mx;

void dfs(int a, int b) {
	for (int i = 0; i < G[a].size(); i++) {
		if (G[a][i].first != b) {
			int j = G[a][i].first;
			d[j] = d[a] + G[a][i].second;
			if (d[j] > d[mx])
				mx = j;
			dfs(j, a);
		}
	}
}
void dfs1(int a, int b) {
	for (int i = 0; i < G[a].size(); i++) {
		if (G[a][i].first != b) {
			int j = G[a][i].first;
			d1[j] = d1[a] + G[a][i].second;
			if (d1[j] > d1[mx])
				mx = j;
			dfs1(j, a);
		}
	}
}

void stinit() {
	for (int i = 1; i <= n; i++) {
		dp[i][0] = val[i];
		dp1[i][0] = val[i];
	}
	for (int i = 1; (1 << i) <= n; i++) {
		for (int j = 1; j + (1 << i)-1 <= n; j++) {
			dp[j][i] = max(dp[j][i - 1], dp[j + (1 << i - 1)][i - 1]);
			dp1[j][i] = min(dp1[j][i - 1], dp1[j + (1 << i - 1)][i - 1]);
		}
	}
}

int lo(int ll, int rr) {
	int lenn = -1;
	int tt = rr - ll + 1;
	while (tt)
	{
		lenn++;
		tt = tt >> 1;
	}
	return lenn;
}

int stfun(int l,int r) {
	int k = lo(l,r);
	return max(dp[l][k], dp[r - (1 << k) + 1][k]) - min(dp1[l][k], dp1[r - (1 << k) + 1][k]);
}

int main() {
	while (scanf("%d%d", &n, &m) != EOF) {
		if (n == 0 && m == 0)
			break;
		for (int i = 0; i <= n; i++) {
			G[i].clear();
		}
		for (int i = 1,a,b,t; i < n; i++) {
			scanf("%d%d%d", &a, &b, &t);
			G[a].push_back({ b,t });
			G[b].push_back({ a,t });
		}
		mx = 0;
		memset(d, 0, sizeof(d));
		dfs(1, 0);
		memset(d, 0, sizeof(d));
		dfs(mx, 0);
		memset(d1, 0, sizeof(d1));
		dfs1(mx, 0);
		for (int i = 1; i <= n; i++) {
			val[i] = max(d[i], d1[i]);
		}
		stinit();
		for (int j = 1,q; j <= m; j++) {
			scanf("%d", &q);
			int index = 1, ans = 1;
			for (int i = 1; i <= n; i++) {
				while (index <= i) {
					if (stfun(index, i) > q) {
						index++;
					}
					else
						break;
				}
				ans = max(ans, i - index + 1);
			}
			printf("%d\n", ans);
		}
	}
	return 0;
}

 之前的错误代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 5e4 + 5;
int n, m;
vector<pair<int, int>>G[N];
int d[N], val[N], dp[N][30], dp1[N][30];
int mx;

void dfs(int a, int b) {
    for (int i = 0; i < G[a].size(); i++) {
        if (G[a][i].first != b) {
            int j = G[a][i].first;
            d[j] = d[a] + G[a][i].second;
            if (d[j] > d[mx])
                mx = j;
            dfs(j, a);
        }
    }
}

void stinit() {
    for (int i = 1; i <= n; i++) {
        dp[i][0] = val[i];
        dp1[i][0] = val[i];
    }
    for (int i = 1; (1 << i) <= n; i++) {
        for (int j = 1; j + (1 << i) - 1 <= n; j++) {
            dp[j][i] = max(dp[j][i - 1], dp[j + (1 << i - 1)][i - 1]);
            dp1[j][i] = min(dp1[j][i - 1], dp1[j + (1 << i - 1)][i - 1]);
        }
    }
}

int stfun(int l, int r) {
    int k = log2(r - l + 1);//错误点:会爆时间,这是个大坑,我卡在这20几次
    return max(dp[l][k], dp[r - (1 << k) + 1][k]) - min(dp1[l][k], dp1[r - (1 << k) + 1][k]);
}

int main() {
    while (scanf("%d%d", &n, &m) != EOF) {
        if (n == 0 && m == 0)
            break;
        for (int i = 1; i <= n; i++) {
            G[i].clear();
        }
        for (int i = 1, a, b, t; i < n; i++) {
            scanf("%d%d%d", &a, &b, &t);
            G[a].push_back({ b,t });
            G[b].push_back({ a,t });
        }
        mx = 0;
        memset(d, 0, sizeof(d));
        dfs(1, 0);
        memset(d, 0, sizeof(d));
        dfs(mx, 0);
        for (int i = 1; i <= n; i++) {
            //错误点:d[mx] - d[i]是错误的,只有在直线的情况下是对的,再有分支的情况下是错的
            val[i] = max(d[mx] - d[i], d[i]);
        }

        stinit();
        for (int j = 1, q; j <= m; j++) {
            scanf("%d", &q);
            int index = 1, ans = 1;
            for (int i = 1; i <= n; i++) {
                while (index <= i) {
                    if (stfun(index, i) > q) {
                        index++;
                    }
                    else
                        break;
                }
                ans = max(ans, i - index + 1);
            }
            printf("%d\n", ans);
        }
    }
    return 0;
}

                      C-湫湫系列故事——设计风景线   

Problem - 4514 (hdu.edu.cn)

Problem Description

  随着杭州西湖的知名度的进一步提升,园林规划专家湫湫希望设计出一条新的经典观光线路,根据老板马小腾的指示,新的风景线最好能建成环形,如果没有条件建成环形,那就建的越长越好。
  现在已经勘探确定了n个位置可以用来建设,在它们之间也勘探确定了m条可以设计的路线以及他们的长度。请问是否能够建成环形的风景线?如果不能,风景线最长能够达到多少?
  其中,可以兴建的路线均是双向的,他们之间的长度均大于0。

Input

  测试数据有多组,每组测试数据的第一行有两个数字n, m,其含义参见题目描述;
  接下去m行,每行3个数字u v w,分别代表这条线路的起点,终点和长度。

  [Technical Specification]
  1. n<=100000
  2. m <= 1000000
  3. 1<= u, v <= n
  4. w <= 1000

Output

  对于每组测试数据,如果能够建成环形(并不需要连接上去全部的风景点),那么输出YES,否则输出最长的长度,每组数据输出一行。

Sample Input

3 3

1 2 1

2 3 1

3 1 1

Sample Output

YES

解析:并查集,树的直径

这道题可以用用并查集来判断是否有环:如果一条边的两个端点的祖先相同,即find(fa[a])==find(fa[b]),那么就有环(可以想象一个三角形来理解)。

如果没有环,则找出树最大的直径,题目很坑,他没有告诉你有多个连通分支,具体为:将所有点扫一遍,当fa[i]==i时说明找到一个连通分支,使用一次找树直径的算法,最后从所有直径中输出最大的直径

这道题到处都是坑,首先是卡时间,卡空间,甚至连编译器都卡,下面这个代码如果选择G++编译器就会爆内存,选择c++编译器勉强能过,但如果不注意还是会爆时间(别问我为什么会这么清楚,我把所有的坑都踩了一边)。还有,这道题的样例一点用都没有。

如果要用G++编译器一定要加上


#pragma comment (linker,"/STACK:102400000,102400000")

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5, M = 1e6 + 5;
int n, m;
vector<pair<int, int>>G[N];
int d[N], fa[N];
int mx,len,pos,x;


int find(int a) {
    if (fa[a] == a)
        return a;
    return fa[a] = find(fa[a]);
}

void dfs(int a, int b) {
    
    for (int i = 0; i<G[a].size(); i++) {
        if (G[a][i].first != b) {
            int j = G[a][i].first;
            d[j] = d[a] + G[a][i].second;
            if (d[mx] < d[j])
                mx = j;
            dfs(j, a);
        }
    }
}
int main() {
    while (cin >> n >> m) {
        int f = 0;
        for (int i = 1; i <= n; i++) {
            fa[i] = i;
            G[i].clear();
        }
        for (int i = 1, a, b, t, t1, t2; i <= m; i++) {
            scanf("%d%d%d", &a, &b, &t);
            t1 = find(a);
            t2 = find(b);
            if (t1 == t2)
                f = 1;
          
            fa[t1] = t2;
            G[a].push_back({ b,t });
            G[b].push_back({ a,t });
        }
        if (f == 1) {
            cout << "YES" << endl;
            continue;
        }

        int ans = 0;
        for (int i = 1; i <= n; i++) {
            if (find(fa[i]) == i) {
                memset(d, 0, sizeof(d));
                mx = 0;
                d[i] = 0;
                dfs(i, 0);
                memset(d, 0, sizeof(d));
                dfs(mx, 0);
                ans = max(ans, d[mx]);
            }
        }
        cout << ans << endl;
      
    }
    return 0;
}

在这道题别用结构体数组来写邻接表,会爆内存,展示一个错误代码,以此为戒

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5, M = 2e6 + 5;
int n, m, cnt;
struct edge {
    int to, w, next;
}edge[M];
int head[N], d[N], fa[N];
int mx;

void add(int a, int b, int w) {
    edge[++cnt].to = b;
    edge[cnt].w = w;
    edge[cnt].next = head[a];
    head[a] = cnt;
}

int find(int a) {
    if (fa[a] == a)
        return a;
    return fa[a] = find(fa[a]);
}

void dfs(int a, int b) {
    for (int i = head[a]; i; i = edge[i].next) {
        if (edge[i].to != b) {
            int j = edge[i].to;
            d[j] = d[a] + 1;
            if (d[mx] < d[j])
                mx = j;
            dfs(j, a);
        }
    }
}

int main() {
    while (cin >> n >> m) {
        int f = 0;
        memset(edge, 0, sizeof(edge));
        memset(head, 0, sizeof(head));
        for (int i = 1; i < n; i++)
            fa[i] = i;
        memset(d, 0, sizeof(d));
        cnt = 0;
        for (int i = 1, a, b, t, t1, t2; i <= m; i++) {
            scanf("%d%d%d", &a, &b, &t);
            t1 = find(fa[a]);
            t2 = find(fa[b]);
            if (t1 == t2)
                f = 1;
            else
                fa[t1] = t2;
            add(a, b, t);
            add(b, a, t);
        }
        if (f == 1) {
            cout << "YES" << endl;
            continue;
        }
        int t = fa[1];
        d[fa[1]] = 0;
        dfs(fa[1], 0);
        d[mx] = 0;
        dfs(mx, 0);
        int ans = mx;
        for (int i = 1; i <= n; i++) {
            if (fa[i] != t) {
                d[fa[i]] = 0;
                dfs(fa[i], 0);
                d[mx] = 0;
                dfs(mx, 0);
            }
            if (d[mx] > d[ans]) {
                ans = mx;
            }
        }
        cout << d[ans] << endl;
    }
    return 0;
}

                          F - Milk Visits S 

P5836 [USACO19DEC] Milk Visits S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Description

Farmer John 计划建造 N 个农场,用 N−1 条道路连接,构成一棵树(也就是说,所有农场之间都互相可以到达,并且没有环)。每个农场有一头奶牛,品种为更赛牛或荷斯坦牛之一。

Farmer John 的 M 个朋友经常前来拜访他。在朋友 i 拜访之时,Farmer John 会与他的朋友沿着从农场Ai​ 到农场 Bi​ 之间的唯一路径行走(可能有 Ai​=Bi​)。除此之外,他们还可以品尝他们经过的路径上任意一头奶牛的牛奶。由于 Farmer John 的朋友们大多数也是农场主,他们对牛奶有着极强的偏好。他的有些朋友只喝更赛牛的牛奶,其余的只喝荷斯坦牛的牛奶。任何 Farmer John 的朋友只有在他们访问时能喝到他们偏好的牛奶才会高兴。

请求出每个朋友在拜访过后是否会高兴。

Input

输入的第一行包含两个整数 N 和 M。

第二行包含一个长为 N 的字符串。如果第 i 个农场中的奶牛是更赛牛,则字符串中第 i 个字符为 G,如果第 i 个农场中的奶牛是荷斯坦牛则为 H

以下 N−1 行,每行包含两个不同的整数 X 和 Y(1≤X,Y≤N),表示农场 X 与 Y 之间有一条道路。

以下 M 行,每行包含整数 Ai​,Bi​,以及一个字符 Ci​。Ai​ 和 Bi​ 表示朋友 i 拜访时行走的路径的端点,Ci​ 是 G 或 H 之一,表示第 i 个朋友喜欢更赛牛的牛奶或是荷斯坦牛的牛奶。

Output

输出一个长为 M 的二进制字符串。如果第 i 个朋友会感到高兴,则字符串的第 i 个字符为 1,否则为 0

Sample 1

InputcopyOutputcopy
5 5
HHGHG
1 2
2 3
2 4
1 5
1 4 H
1 4 G
1 3 G
1 3 H
5 5 H
10110

Hint

在这里,从农场 1 到农场 4 的路径包括农场 1、2 和 4。所有这些农场里都是荷斯坦牛,所以第一个朋友会感到满意,而第二个朋友不会。

关于部分分:

测试点 11 样例。

测试点 2∼52∼5 满足 N≤103,M≤2⋅103。

对于 100%100% 的数据,1≤N≤105,1≤M≤105。

供题:Spencer Compton

解析:LCA,累加

如果A到B之间H的个数等于经过的点数,那么说明这几个牧场全是H,如果A,B之间H的个数为零,则说明这几个牧场全是Q,所以懂啦?

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 5e5 + 5;
int n, m, cnt;
struct edge {
	int to, next;
}edge[2 * N];
int head[2 * N], arr[N], d[N], dp[N][20], v[N];
char str[N];

void add(int a, int b) {
	edge[++cnt].to = b;
	edge[cnt].next = head[a];
	head[a] = cnt;
}

void dfs(int k) {
	for (int i = 1; (1 << i) < d[k]; i++) {
		dp[k][i] = dp[dp[k][i - 1]][i - 1];
	}
	for (int i = head[k]; i; i = edge[i].next) {
		int j = edge[i].to;
		if (v[j])
			continue;
		v[j] = 1;
		d[j] = d[k] + 1;
		dp[j][0] = k;
		arr[j] = arr[k];
		if (str[j - 1] == 'H')
			arr[j]++;
		dfs(j);
	}
}

int LCA(int a, int b) {
	if (d[a] < d[b])
		swap(a, b);
	int k = log2(d[a] - d[b]);
	for (int i = k; i >= 0; i--) {
		if (d[dp[a][i]] >= d[b]) {
			a = dp[a][i];
		}
	}
	if (a == b)
		return a;
	k = log2(d[a]);
	for (int i = k; i >= 0; i--) {
		if (dp[a][i] == dp[b][i])
			continue;
		a = dp[a][i];
		b = dp[b][i];
	}
	return dp[a][0];
}

int main() {
	scanf("%d%d", &n, &m);
	cin >> str;

	for (int i = 1, a, b; i < n; i++) {
		scanf("%d%d", &a, &b);
		add(a, b);
		add(b, a);
	}
	d[1] = 1;
	v[1] = 1;
	if (str[0] == 'H')
		arr[1] = 1;
	dfs(1);
	int k1, k2;
	string ch;
	while (m--) {
		scanf("%d%d", &k1, &k2);
		cin >> ch;
		int node = LCA(k1, k2);
		int k4 = arr[k1] + arr[k2] - 2 * arr[node];
		int k5 = d[k1] + d[k2] - 2 * d[node] + 1;
		if (str[node - 1] == 'H')
			k4++;
		if (ch == "H") {
			if (k4 != 0)
				cout << "1";
			else
				cout << "0";
		}
		else {
			if (k5 == k4)
				cout << "0";
			else
				cout << "1";
		}
	}
	return 0;
}

                                        G - 机房

P8805 [蓝桥杯 2022 国 B] 机房 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Description

这天,小明在机房学习。

他发现机房里一共有 n 台电脑,编号为 1 到 n,电脑和电脑之间有网线连接,一共有 n−1 根网线将 n 台电脑连接起来使得任意两台电脑都直接或者间接地相连。

小明发现每台电脑转发、发送或者接受信息需要的时间取决于这台电脑和多少台电脑直接相连,而信息在网线中的传播时间可以忽略。比如如果某台电脑用网线直接连接了另外 d 台电脑, 那么任何经过这台电脑的信息都会延迟 d 单位时间 (发送方和接收方也会产生这样的延迟,当然如果发送方和接收方都是 同一台电脑就只会产生一次延迟)。

小明一共产生了 m 个疑问:如果电脑 ui​ 向电脑vi​ 发送信息,那么信息从 ui​ 传到 vi​ 的最短时间是多少?

Input

输入共 n+m 行,第一行为两个正整数n,m 。

后面 n−1 行,每行两个正整数 x,y 表示编号为 x 和 y 的两台电脑用网线 直接相连。

后面 m 行,每行两个正整数ui​,vi​ 表示小明的第 i 个疑问。

Output

输出共 m 行,第 i 行一个正整数表示小明第 i 个疑问的答案。

Sample 1

InputcopyOutputcopy
4 3
1 2
1 3
2 4
2 3
3 4
3 3
5
6
1

Hint

【样例说明】

这四台电脑各自的延迟分别为 2,2,1,12,2,1,1 。

对于第一个询问, 从 22 到 33 需要经过 2,1,32,1,3, 所以时间和为 2+2+1=52+2+1=5。对于第二个询问,从 33 到 44 需要经过 3,1,2,43,1,2,4,所以时间和为 1+2+2+1=61+2+2+1=6。

对于第三个询问,从 33 到 33 只会产生一次延迟, 所以时间为 1。

【评测用例规模与约定】

对于 30%30% 的数据,保证 n,m≤1000;

对于 100%100% 的数据,保证 n,m≤105。

蓝桥杯 2022 国赛 B 组 H 题。

解析:LCA,累加

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 5e5 + 5;
int n, m, cnt;
struct edge {
	int to, next;
}edge[2 * N];
int head[2 * N], d[N], dp[N][20], v[N];
LL arr[N], yrr[N];
void add(int a, int b) {
	edge[++cnt].to = b;
	edge[cnt].next = head[a];
	head[a] = cnt;
}

void dfs(int k) {
	for (int i = 1; (1 << i) < d[k]; i++) {
		dp[k][i] = dp[dp[k][i - 1]][i - 1];
	}
	for (int i = head[k]; i; i = edge[i].next) {
		int j = edge[i].to;
		if (v[j])
			continue;
		v[j] = 1;
		d[j] = d[k] + 1;
		dp[j][0] = k;
		arr[j] += arr[k];
		dfs(j);
	}
}

int LCA(int a, int b) {
	if (d[a] < d[b])
		swap(a, b);
	int k = log2(d[a] - d[b]);
	for (int i = k; i >= 0; i--) {
		if (d[dp[a][i]] >= d[b]) {
			a = dp[a][i];
		}
	}
	if (a == b)
		return a;
	k = log2(d[a]);
	for (int i = k; i >= 0; i--) {
		if (dp[a][i] == dp[b][i])
			continue;
		a = dp[a][i];
		b = dp[b][i];
	}
	return dp[a][0];
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1, a, b; i < n; i++) {
		scanf("%d%d", &a, &b);
		add(a, b);
		add(b, a);
		arr[a]++;
		arr[b]++;
		yrr[a] = arr[a];
		yrr[b] = arr[b];
	}
	d[1] = 1;
	v[1] = 1;
	dfs(1);
	int k1, k2, ans;
	while (m--) {
		scanf("%d%d", &k1, &k2);
		int ret = LCA(k1, k2);
		cout << arr[k1] + arr[k2] - 2 * arr[ret] + yrr[ret] << endl;
	}
	return 0;
}

                                   H - 松鼠的新家

P3258 [JLOI2014] 松鼠的新家 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Description

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

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

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

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

Input

第一行一个正整数 n,表示房间个数第二行 n 个正整数,依次描述 a1​,a2​,⋯,an​。

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

Output

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

Sample 1

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

Hint

对于全部的数据,2≤n≤3×10^5,1≤ai​≤n。

解析:LCA与树形差分

这道题很容易能看出来是一道LCA的题,至少LCA可以解这道题,但我们应该如何解决记录经过的房间次数呢?这里我们使用树形差,于是这道题就变成了LCA与树形差分的综合应用题了。

有了思路,我们呢接下来上代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 3e5 + 5;
int n, m, cnt;
struct edge {
	int to, next;
}edge[2*N];
int head[2 * N], d[N], dp[N][20], v[N];
int arr[N],brr[N];

void add(int a, int b) {
	edge[++cnt].to = b;
	edge[cnt].next = head[a];
	head[a] = cnt;
}

void dfs(int k) {
	for (int i = 1; (1 << i) < d[k]; i++) {
		dp[k][i] = dp[dp[k][i - 1]][i - 1];
	}
	for (int i = head[k]; i; i = edge[i].next) {
		int j = edge[i].to;
		if (v[j])
			continue;
		v[j] = 1;
		d[j] = d[k] + 1;
		dp[j][0] = k;
		dfs(j);
	}
}

int LCA(int a, int b) {
	if (d[a] < d[b])
		swap(a, b);
	int k = log2(d[a] - d[b]);
	for (int i = k; i >= 0; i--) {
		if (d[dp[a][i]] >= d[b]) {
			a = dp[a][i];
		}
	}
	if (a == b)
		return a;
	k = log2(d[a]);
	for (int i = k; i >= 0; i--) {
		if (dp[a][i] == dp[b][i])
			continue;
		a = dp[a][i];
		b = dp[b][i];
	}
	return dp[a][0];
}

void DFS(int a,int b) {
	for (int i = head[a]; i; i = edge[i].next) {
		int j = edge[i].to;
		if (j == b)
			continue;
		DFS(j, a);
		arr[a] += arr[j];
	}
}

int main(){
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &brr[i]);
	}
	for (int i = 1, a, b; i < n; i++) {
		scanf("%d%d", &a, &b);
		add(a, b);
		add(b, a);
	}
	d[1] = 1;
	v[1] = 1;
	dfs(1);
	
	for (int i = 2; i <= n; i++) {
		int ret = LCA(brr[i], brr[i - 1]);
		arr[brr[i]]++;
		arr[brr[i - 1]]++;
		arr[ret]--;
		arr[dp[ret][0]]--;
	}
	/*for (int i = 1; i <= n; i++) {
		printf("%d\n", arr[i]);
	}
	cout << "?????????" << endl;*/
	DFS(1,0);
	for (int i = 2; i <= n; i++) {
		arr[brr[i]]--;
	}
	for (int i = 1; i <= n; i++) {
		printf("%d\n", arr[i]);
	}
	return 0;
}

注意差分的细节!

                                       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值