首页 > 其他分享 >2021牛客OI赛前集训营-提高组(第二场)第三题 树数树题解

2021牛客OI赛前集训营-提高组(第二场)第三题 树数树题解

时间:2023-04-25 18:11:36浏览次数:37  
标签:集训营 int 题解 tr pos long mpos 树数树 mx

题目描述

牛牛有一棵 \(n\) 个点的有根树,根为 \(1\)。
我们称一个长度为 \(m\) 的序列 \(a\) 是好的,当且仅当:

  • \(\forall i \in (1,m]\),\(a_i\)为 \(a_{i−1}\)的祖先或 \(a_{i−1}\)是 \(ai\)的祖先
  • \(\forall 1 \leq i \lt j \leq m, a_i \neq a_j\)

你需要帮助牛牛求出最长的序列长度。

输入格式

第一行一个正整数 \(T\),表示数据组数。
对于每组数据第一行一个正整数 \(n\)。
接下来 \(n − 1\)行,每行两个正整数 \(u,v\),表示树上的一条边。

输出格式

\(T\) 行,每行一个整数表示每组数据的答案。

样例

输入样例1

18
5 3
1 5
4 5
2 5
1 6
8 7
7 6

输出样例1

7

数据范围

对于 100% 的数据,\(1 ≤ T ≤ 5,2 ≤ n ≤ 10^5,1 ≤ u, v ≤ n,u ≠ v,\) 输入保证是一棵树

简化题意

有一棵树,你可以跳到一个祖先,也可以跳到一个子孙,不能走重复的点,求最长路径。

正解

一开始看错题了,以为祖先=父亲,那就是树的直径!脑残题!

越看越不对劲......大家好好读题啊......

容易想到一个树形DP,我们设置 \(f_x\) 为 \(x\) 的子树的最长路径长度。

那么我们的走法显然是 \(A->x->B\) 其中的 \(A,B\) 表示 \(x\) 中的一部分。

显然我们应该让 \(f_A+f_B\) 最大化,此时,我们有动态转移方程:

\[f_x=f_A+f_B+1 \]

然后这里有一个误区,就是 \(A,B\) 一定是 \(x\) 的孩子,其实不然,有可能,\(f_A\) 并不是 \(A\) 的子树,还会缺少一块未选择的部分。

而这个部分很大,比 \(x\)的其他孩子都大,这时候,可以选择这个部分。

那这里我们怎么维护呢?

不难发现,其实我们只需要找出 \(x\) 子树的最大的 \(f_A\) 和 次大的\(f_B\)值作为答案,可以考虑使用 DFS 序+线段树维护。

注意:选择之后要将这个值在线段树上清零吗,防止在下一次选择中被选择。

然后,我当了一回大聪明:

虽然丰富人生阅历,但是时间慢+代码长+空间大,我现在感觉我是弱智......

我们的时间复杂度是 \(O(n\log n)\),比启发式合并优秀。

比较正常的代码

#include <bits/stdc++.h>
using namespace std;
struct node {
    int nxt, to;
} a[200005];
struct tree {
    int l, r, mx, mpos;
} tr[400005];
int t, n, h[100005], tot, x, y, f[100005], ans, num[100005], num2[100005], sz[100005], cnt;
void build(int pos, int l, int r) {
    tr[pos].l = l, tr[pos].r = r;
    if (l == r) {
        tr[pos].mpos = l;
        return;
    }
    int mid = (l + r) / 2;
    build(pos * 2, l, mid);
    build(pos * 2 + 1, mid + 1, r);
    tr[pos].mpos = l;
}
void update(int pos, int x, int y) {
    if (x < tr[pos].l || tr[pos].r < x)
        return;
    if (tr[pos].l == tr[pos].r && tr[pos].l == x) {
        tr[pos].mx = y;
        return;
    }
    update(pos * 2, x, y);
    update(pos * 2 + 1, x, y);
    if (tr[pos * 2].mx > tr[pos * 2 + 1].mx) {
        tr[pos].mx = tr[pos * 2].mx;
        tr[pos].mpos = tr[pos * 2].mpos;
    } else {
        tr[pos].mx = tr[pos * 2 + 1].mx;
        tr[pos].mpos = tr[pos * 2 + 1].mpos;
    }
}
int query(int pos, int l, int r) {
    if (r < tr[pos].l || tr[pos].r < l)
        return 0;
    if (l <= tr[pos].l && tr[pos].r <= r) {
        return tr[pos].mpos;
    }
    int lm = query(pos * 2, l, r), rm = query(pos * 2 + 1, l, r);
    if (f[num2[lm]] > f[num2[rm]])
        return lm;
    else
        return rm;
}
void add(int x, int y) {
    tot++;
    a[tot].to = y;
    a[tot].nxt = h[x];
    h[x] = tot;
}
void dfs(int x, int fa) {
    num[x] = ++cnt, num2[cnt] = x;
    for (int i = h[x]; i; i = a[i].nxt) {
        if (a[i].to == fa)
            continue;
        dfs(a[i].to, x);
        sz[x] += sz[a[i].to];
    }
    sz[x]++;
}
void dfs2(int x, int fa) {
    for (int i = h[x]; i; i = a[i].nxt) {
        if (a[i].to == fa)
            continue;
        dfs2(a[i].to, x);
    }
    int mx = 0, sum = 0;
    for (int i = 1; i <= 2; i++) {
        mx = query(1, num[x], num[x] + sz[x] - 1);
        if (num2[mx] != x && f[num2[mx]] != 0) {
            sum += f[num2[mx]];
            f[num2[mx]] = 0;
            update(1, mx, 0);
        }
    }
    f[x] = 1 + sum, ans = max(ans, f[x]);
    update(1, num[x], f[x]);
}
int main() {
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        memset(h, 0, sizeof(h));
        memset(a, 0, sizeof(a));
        memset(f, 0, sizeof(f));
        memset(tr, 0, sizeof(tr));
        memset(sz, 0, sizeof(sz));
        tot = 0, ans = 0, cnt = 0;
        for (int i = 1; i <= n - 1; i++) {
            scanf("%d%d", &x, &y);
            add(x, y);
            add(y, x);
        }
        build(1, 1, n), dfs(1, 0), dfs2(1, 0);
        printf("%d\n", ans);
    }
}

考场代码

#include<bits/stdc++.h>
using namespace std;
struct node
{
	long long nxt,to;
}a[200005];
struct tree
{
	long long l,r,mx,mpos;
}tr[1000005];
long long t,n,h[100005],tot,x,y,f[100005],ans,vis[100005],num[100005],num2[100005],sz[100005],cnt;
stack<pair<long long,long long> >s;
void build(long long pos,long long l,long long r)
{	
	tr[pos].l=l,tr[pos].r=r;
	if(l==r)
	{	
		tr[pos].mpos=l;
		return;	
	}
	long long mid=(l+r)/2;
	build(pos*2,l,mid);
	build(pos*2+1,mid+1,r);
	tr[pos].mpos=l;
}
void update(long long pos,long long x,long long y)
{
	if(x<tr[pos].l||tr[pos].r<x)return;
	if(tr[pos].l==tr[pos].r&&tr[pos].l==x)
	{
		tr[pos].mx=y;
		return;
	}
	update(pos*2,x,y);
	update(pos*2+1,x,y);
	if(tr[pos*2].mx>tr[pos*2+1].mx)
	{
		tr[pos].mx=tr[pos*2].mx;
		tr[pos].mpos=tr[pos*2].mpos;
	}
	else
	{
		tr[pos].mx=tr[pos*2+1].mx;
		tr[pos].mpos=tr[pos*2+1].mpos;		
	}
}
long long query(long long pos,long long l,long long r)
{
	if(r<tr[pos].l||tr[pos].r<l)return 0;
	if(l<=tr[pos].l&&tr[pos].r<=r)
	{
		return tr[pos].mpos;
	}
	long long lm=query(pos*2,l,r),rm=query(pos*2+1,l,r);
	if(f[num2[lm]]>f[num2[rm]])return lm;
	else return rm;
}
void add(long long x,long long y)
{
	tot++;
	a[tot].to=y;
	a[tot].nxt=h[x];
	h[x]=tot;
}
int main()
{
	scanf("%lld",&t);
	while(t--)
	{
		scanf("%lld",&n);
		memset(h,0,sizeof(h));
		memset(a,0,sizeof(a));
		memset(f,0,sizeof(f));
		memset(vis,0,sizeof(vis));
		memset(tr,0,sizeof(tr));
		memset(sz,0,sizeof(sz));
		memset(num,0,sizeof(num));
		memset(num2,0,sizeof(num2));
		while(!s.empty())s.pop();
		tot=0,ans=0,cnt=0;
		for(int i=1;i<=n-1;i++)
		{
			scanf("%lld%lld",&x,&y);
			add(x,y);
			add(y,x); 
		} 
		s.push({1,0});
		while(!s.empty())
		{	
			long long x=s.top().first,fa=s.top().second;
			if(num[x]==0)num[x]=++cnt,num2[cnt]=x;
			for(int i=h[x];i;i=a[i].nxt)
			{	
				if(a[i].to==fa)continue;
				if(!vis[a[i].to])s.push({a[i].to,x});
			}
			if(s.top().first==x)
			{
				for(int i=h[x];i;i=a[i].nxt)
				{	
					if(a[i].to==fa)continue;
					sz[x]+=sz[a[i].to];
				}
				sz[x]++,vis[x]=1;
				s.pop();
			}
		}
		memset(vis,0,sizeof(vis));
		build(1,1,n);
		s.push({1,0});
		while(!s.empty())
		{	
			long long x=s.top().first,fa=s.top().second;
			for(int i=h[x];i;i=a[i].nxt)
			{	
				if(a[i].to==fa)continue;
				if(!vis[a[i].to])s.push({a[i].to,x});
			}
			if(s.top().first==x)
			{	
				long long mx=0,sum=0;
				for(int i=1;i<=2;i++)
				{
					mx=query(1,num[x],num[x]+sz[x]-1);
					if(num2[mx]!=x&&f[num2[mx]]!=0)
					{
						sum+=f[num2[mx]];
						f[num2[mx]]=0;
						update(1,mx,0);	
					}					
				}
				f[x]=1+sum;
				ans=max(ans,f[x]);
				update(1,num[x],f[x]);
				vis[x]=1,s.pop();
			}
		}
		printf("%lld\n",ans);
	}
}

标签:集训营,int,题解,tr,pos,long,mpos,树数树,mx
From: https://www.cnblogs.com/I-am-joker/p/17353451.html

相关文章

  • 2021牛客OI赛前集训营-提高组(第三场) 第二题 交替 题解与结论证明
    题目描述一个长度为\(n\)的数组\(A\),每秒都会变成一个长度为\(n−1\)新数组\(A'\),其变化规则如下:若当前数组\(A\)的长度\(n\)为偶数,则对于新数组\(A'\)的每一个位置\(i(1≤i<n)\)来说,\(A'[i]=A[i]+A[i+1]\)若当前数组\(A\)的长度\(n\)为奇数,则对于......
  • 【SD集训】20230425 T2 差(difference) 题解 CF1500F 【Cupboards Jumps】
    大家可以猜猜看为什么有两个标题,因为这个原因本文就不设密码了,被He_ren的原题创到了。吐槽一下,He_ren甚至出原题还用脚造数据,虽然数据确实比较难造。不过那两个\(O(n^2)\)老哥好像都没最后将所有数调整成非负,遗憾20。有人场切*3500却没过签到题,我不说是谁。题目描述......
  • Net6+axios 返回401 axios不能获取 状态码问题解决
    错误使用app.UseAuthentication();//认证 这里要加,位置不能反app.UseAuthorization();//授权 app.UseCors();//启用Cors解决方法app.UseCors();//启用Corsapp.UseAuthentication();//认证 这里要加,位置不能反app.UseAuthorization();//授权  更换前更换后  ......
  • 题解:【CTS2022】 独立集问题
    题目链接来自2023SDPT-Round1-Day4课上Qingyu的讲解。考虑对于一个点多次操作会发生什么?第一次操作会将周围的点的权值吸过来,自己对答案的贡献乘\(-1\),周围的点的贡献乘\(+1\),得到新的权值\(a_x'=\pma_x\mp\sum_{y\inson_x}a_y\);再一次操作,我们可以将这个新的贡......
  • Mount cifs存储时提示not supported问题解决
    Mountcifs存储时提示notsupported问题解决:报错: mounterror(95):Operationnotsupported排查:1、当时刚好是mount2个存储,结果1个可以1个不行,就反馈给负责存储同事查看2个存储的区别2、存储同事查看后得出不行的是比较老的Netapp存储,mounterror(95)错误应该是不支持的smb协议......
  • leetcode-217-存在重复元素 题解
    题目描述给你一个整数数组nums。如果任一值在数组中出现至少两次,返回true;如果数组中每个元素互不相同,返回false。示例1:输入:nums=[1,2,3,1]输出:true示例2:输入:nums=[1,2,3,4]输出:false示例3:输入:nums=[1,1,1,3,3,4,3,2,4,2]输出:true提......
  • P9228 原神 题解
    题目传送门题目大意有一个魔法师,她可以用火元素攻击魔法把对附着冰元素的怪物的伤害\(\times2\),用冰元素攻击魔法把对附着火元素的怪物的伤害\(+5\)。每个怪物初始时没有附着任何元素,给出冰、火元素对每个怪物的初始伤害,魔法师可以任意安排攻击顺序,求最大总伤害。解题思路......
  • CF题解
    E.RearrangeBrackets2100括号树gq!https://codeforces.com/contest/1821/problem/E题解:若我们把序列看作是一个由匹配括号组成的森林,外层括号是内层括号的父亲,则整个正则括号序列的cost可以看作是森林中所有点的深度之和,根节点深度为0,故每次操作可以看作是将某棵树的父节点......
  • 跨域问题解决、其他权限校验方法
    跨域问题解决浏览器出于安全的考虑,使用XMLHttpRequest对象发起HTTP请求时必须遵守同源策略,否则就是跨域的HTTP请求,默认情况下是被禁止的。同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。前后端分离项目前端项目和后端项目一般都不是同源的,所以肯定会存在......
  • 2023年团体程序设计天梯赛 题解
    仅更新L1,L2随后写**更好的阅读体验:2023年团体程序设计天梯赛题解**L1-1最好的文档有一位软件工程师说过一句很有道理的话:“Goodcodeisitsownbestdocumentation.”(好代码本身就是最好的文档)。本题就请你直接在屏幕上输出这句话。输入格式:本题没有输入。输出格式:在一行中输出......