树的有关知识

Godfather

树的重心

const int N = 500010;
int h[N], e[N], ne[N], idx;
bool vis[N];
vector<PII>ans;//second是点的编号,first是划去这个点后剩下最大连通块的大小是多少
int n,root;
int d[N];
void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfs(int u)
{
	vis[u] = true;//sum是以当前u为根节点的子树大小,anss是划掉u这个节点后剩下连通块的最大值
	int sum = 1, anss = 0;
	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if (!vis[j])
		{
			int s = dfs(j);
			anss = max(s, anss);
			sum += s;
		}	
	}
	anss = max(anss, n - sum);
	ans.push_back(make_pair(anss, u));
	return sum;
}
int main()
{
	n = read();
	memset(h, -1, sizeof h);
	for (int i = 0; i < n - 1; i++)
	{
		int a, b;
		a = read(), b = read();
		add(a, b), add(b, a);
		d[b]++;
	}
	for(int i=1;i<=n;i++)
		if (!d[i])
		{
			root = i;
			break;
		}
	dfs(root);
	sort(ans.begin(), ans.end());//按划掉这个点后剩下最大连通块大小从小到大排序
	int i = 0;
	while (1)//打印答案直到不同就退出
	{
		cout << ans[i].second<<" ";
		if (ans[i].first != ans[i + 1].first)break;
		i++;
	}
}

Cow Marathon

树的直径

const int N = 50010, M = 250000;
int h[N], e[M], ne[M], w[M], idx;
int d[N];
bool vis[N];
int n, m;
int root;
int dp[N];//dp[i]即i这个点到它的子树上最远的点的距离是多少
int ans;
void dfs(int x)
{
	vis[x] = 1;
	for (int i = h[x]; ~i; i = ne[i])
	{
		int j = e[i];
		if (!vis[j])
		{
			dfs(j);//先dfs再做后续操作
			ans = max(ans, dp[x]+dp[j]+w[i]);//先让ans取max再更新dp[x]就可以避免ans中计算的dp[x]和dp[j]在同一条路径上的情况;
			dp[x] = max(dp[x], dp[j] + w[i]);
		}
	}
}
void add(int a, int b, int c)
{
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int main()
{
	n = read(), m = read();
	memset(h, -1, sizeof h);
	while (m--)
	{
		int a, b, c;
		a = read(), b = read(), c = read();
		add(a, b, c), add(b, a, c);
		d[b]++;
	}
	for (int i = 1; i <= n; i++)
	{
		if (!d[i])
		{
			root = i;
			break;
		}
	}
	dfs(root);
	cout << ans;
}

Farthest tree node in tree (2)

树的中心

const int N = 30010, M = 60010;
int h[N], e[M], ne[M], w[M], idx;
int d[N];
bool vis[N];
int n;
int root;
int dp[N][3];//0是到子树的最远点的距离,1是到子树的次远点的距离,2是到除子树外(即往上)的最远点距离
void dfs1(int x)
{
	vis[x] = 1;
	for (int i = h[x]; ~i; i = ne[i])
	{
		int j = e[i];
		if (!vis[j])
		{
			dfs1(j);//先递归再做其他操作
			if (dp[j][0] + w[i] >= dp[x][0])//更新到子树的最大值和次大值
			{
				dp[x][1] = dp[x][0];
				dp[x][0] = dp[j][0] + w[i];
			}
			else if (dp[j][0] + w[i] >= dp[x][1])
				dp[x][1] = dp[j][0] + w[i];
		}
	}
}
void dfs2(int x)
{
	vis[x] = 1;
	for (int i = h[x]; ~i; i = ne[i])
	{
		int j = e[i];
		if (!vis[j])
		{
			if (w[i] + dp[j][0] == dp[x][0])//说明父亲节点的往子树最大值和本节点往子树最大值处在同一路径上,所以只能使用次大值让其不在同一路径上
				dp[j][2] = max(dp[x][2] + w[i], dp[x][1] + w[i]);
			else dp[j][2] = max(dp[x][2] + w[i], dp[x][0] + w[i]);
			dfs2(j);//先做完其他操作再递归

		}
	}
}
void add(int a, int b, int c)
{
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int main()
{
	int t; t = read();
	int cnt = 1;
	while (t--)
	{
		printf("Case %d:\n", cnt++);
		n = read();
		memset(h, -1, sizeof h),idx=0;
		memset(d, 0, sizeof d);
		memset(dp, 0, sizeof dp);
		memset(vis, 0, sizeof vis);

		for (int i = 0; i < n - 1; i++)
		{
			int a, b, c;
			a = read(), b = read(), c = read();
			add(a, b, c), add(b, a, c);
			d[b]++;
		}
		for (int i = 0; i < n; i++)
		{
			if (!d[i])
			{
				root = i;
				break;
			}
		}
		dfs1(root);
		memset(vis, 0, sizeof vis);
		dfs2(root);
		for (int i = 0; i < n; i++)cout << max(dp[i][2],dp[i][0]) << endl;
	}
}

Masha and a Beautiful Tree

link

玛莎认为,如果树叶中的值按照从左到右的递增顺序排列,那么这棵树就很美。在一个操作中,Masha可以选择树的任何非叶顶点,并交换它的左右子结点(以及它们的子树)。

帮助玛莎了解她是否可以在一定数量的操作中使一棵树变得美丽。如果可以,那么输出最小数量的操作,使树变得漂亮。

思路:分治递归模拟二叉树的替换

int s[262190];
int ans;
void dfs(int l, int r)
{
	if (l >= r)return;//递归出口;(l==r是叶子节点)
	int mid = l + r >> 1;
	dfs(l, mid), dfs(mid + 1, r);//先递归左右子树再执行后续操作,则达到从下往上处理树的效果;
	if (s[l] > s[r])//当前树的左端点>右端点则一定需要依次交换两个子树
	{
		ans++;
		for (int i = l; i <= mid; i++)//模拟两个子树的交换
			swap(s[i], s[i + mid + 1 - l]);//mid+1-l就是一个子树的长度,则i+这个长度就能得到左子树的点对应到右子树是哪一个点
	}
}
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		ans = 0;
		int n; cin >> n;
		for (int i = 1; i <= n; i++)scanf("%d", &s[i]);
		dfs(1, n);
		int i;
		for ( i = 1; i < n; i++)//dfs完后循环一遍,若叶子节点仍未排好序则说明不可能使得!
			if (s[i] > s[i + 1])
			{
				cout << "-1" << endl;
				break;
			}
		if (i == n)cout << ans << endl;
	}
	
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值