2021牛客OI赛前集训营-提高组(第二场)第三题 树数树题解
题目描述
牛牛有一棵 \(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\) 最大化,此时,我们有动态转移方程:
\]
然后这里有一个误区,就是 \(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);
}
}
2021牛客OI赛前集训营-提高组(第二场)第三题 树数树题解的更多相关文章
- 2020牛客NOIP赛前集训营-提高组(第三场)C-牛半仙的妹子Tree【虚树,最短路】
正题 题目链接:https://ac.nowcoder.com/acm/contest/7609/C 题目大意 给出\(n\)个点的一棵树,\(m\)个时刻各有一个操作 标记一个点,每个点被标记后的每 ...
- 2020牛客NOIP赛前集训营-普及组(第二场)A-面试
面 试 面试 面试 题目描述 牛牛内推了好多人去牛客网参加面试,面试总共分四轮,每轮的面试官都会对面试者的发挥进行评分.评分有 A B C D 四种.如果面试者在四轮中有一次发挥被评为 D,或者两次发 ...
- 2020牛客NOIP赛前集训营-普及组(第二场) 题解
目录 T1 面试 描述 题目描述 输入描述: 输出描述: 题解 代码 T2 纸牌游戏 描述 题目描述 输入描述: 输出描述: 题解 代码 T3 涨薪 描述 题目描述 输入描述: 输出描述: 题解 代码 ...
- 牛客网NOIP赛前集训营-提高组(第四场)游记
牛客网NOIP赛前集训营-提高组(第四场)游记 动态点分治 题目大意: \(T(t\le10000)\)组询问,求\([l,r]\)中\(k(l,r,k<2^{63})\)的非负整数次幂的数的个 ...
- 牛客网NOIP赛前集训营-提高组(第四场)B区间
牛客网NOIP赛前集训营-提高组(第四场)B区间 题目描述 给出一个序列$ a_1 \dots a_n$. 定义一个区间 \([l,r]\) 是好的,当且仅当这个区间中存在一个 \(i\),使得 ...
- 牛客网NOIP赛前集训营-提高组(第四场)B题 区间
牛客网NOIP赛前集训营-提高组(第四场) 题目描述 给出一个序列 a1, ..., an. 定义一个区间 [l,r] 是好的,当且仅当这个区间中存在一个 i,使得 ai 恰好等于 al, al+1, ...
- 牛客网NOIP赛前集训营-普及组(第二场)和 牛客网NOIP赛前集训营-提高组(第二场)解题报告
目录 牛客网NOIP赛前集训营-普及组(第二场) A 你好诶加币 B 最后一次 C 选择颜色 D 合法括号序列 牛客网NOIP赛前集训营-提高组(第二场) A 方差 B 分糖果 C 集合划分 牛客网N ...
- 牛客网NOIP赛前集训营-提高组(第一场)
牛客的这场比赛感觉真心不错!! 打得还是很过瘾的.水平也比较适合. T1:中位数: 题目描述 小N得到了一个非常神奇的序列A.这个序列长度为N,下标从1开始.A的一个子区间对应一个序列,可以由数对[l ...
- 牛客网NOIP赛前集训营-提高组18/9/9 A-中位数
链接:https://www.nowcoder.com/acm/contest/172/A来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言5242 ...
- 牛客网NOIP赛前集训营-提高组(第二场)A 方差
链接:https://www.nowcoder.com/acm/contest/173/A来源:牛客网 题目描述 一个长度为 m 的序列 b[1...m] ,我们定义它的方差为 ,其中 表示序列的平 ...
随机推荐
- create_generated_clock 的用法
本文转载自:create_generated_clock_亓磊的博客-CSDN博客_create_generated_clock 文章目录 参数source和master_clock区别 create ...
- .NET实验二
实验名称:实验二 面向对象程序设计 一. 实验目的 1. 理解类的定义.继承等面向对象的的基本概念: 2. 掌握 C#语言定义类及其各种成员(字段,属性,方法)的方法: 3. 掌握方法覆盖的应用: 4 ...
- docker中的gitlab数据备份、迁移和升级
前期准备 数据备份 记录gitlab运行命令: docker run -itd --name gitlab \ --hostname gitlab.suniot.cn \ -p 443:443 -p ...
- 安装DevExpress VCL,使用时报错 某单元文件找不到的解决办法
1.新建一个工程做为测试 2.点击projecct-->options 3. 4. 5.在上4图上的红框内加入packages文件 dxCoreRS27;dxGDIPlusRS27;dxComn ...
- .DS_Store泄露利用过程
.DS_Store文件泄漏利用工具: ds_store_exp.DS_Store是Mac下Finder用来保存如何展示 文件/文件夹 的数据文件,每个文件夹下对应一个. 如果开发/设计人员将.DS_S ...
- java 通过反射以及MethodHandle执行泛型参数的静态方法
开发过程中遇到一个不能直接调用泛型工具类的方法,因此需要通过反射来摆脱直接依赖. 被调用静态方法示例 public class test{ public static <T> T get( ...
- psq强制携带-h
背景: pg更新后psql 指令必须携带 -h指令,导致很多直接使用"psql"的shell脚本无法连接. 方案: 1.挨个更新调用psql的地方: 显然太多了,改不过来. 2.新 ...
- Log4j日志框架使用
Log4j是Apache下的一款开源的日志框架,能够满足我们在项目中对于日志记录的需求.一般来讲,在项目中,我们会结合slf4j和log4j一起使用.Log4j提供了简单的API调用,强大的日志格式定 ...
- Less-4 报错注入
补坑:报错注入 当我们 union 无法注入的时候,可以使用报错注入,这里我们有三种报错注入,xpth语法错误和count()+rand()+floor()+group by重复组建错误 extrac ...
- sql处理重复的列,更好理清分组和分区
一.分组统计.分区排名 1.语法和含义: 如果查询结果看得有疑惑,看第二部分-sql处理重复的列,更好理清分组和分区,有建表插入数据的sql语句 分组统计:GROUP BY 结合 统计/聚合函数一起使 ...