牛客多校7-F xay loves trees (dfs序&线段树)

题目

在这里插入图片描述
样例:
输入

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

输出

3
1
2

分析

  1. 选的点在第一棵树上是连续的一条链;而在第二棵树上是互不为祖先,即他们的子树不重叠。
  2. 那么令 ans[x] 为以 x 点为最末端的点,在第一棵树往上最长符合条件的链。在第一棵树上遍历,用双向队列记录集合中的点,然后对第二棵树的dfs序列维护区间即可。
  3. 具体操作见代码。
  4. 时间复杂度是 O(n lgn)。

代码

#include "bits/stdc++.h"
//#include <bitsdc++.h>
using namespace std;
#define MAXN (300010)
int a[MAXN<<2],lazy[MAXN<<2];
#define lchild(x) ((x)<<1)
#define rchild(x) (((x)<<1)|1)
void PushDown(int root,int L,int R)
{
    lazy[lchild(root)] += lazy[root];
    lazy[rchild(root)] += lazy[root];
    a[lchild(root)] += lazy[root];
    a[rchild(root)] += lazy[root];
    lazy[root] = 0;
}
void Add(int root,int L,int R,int l,int r,int val)
{
    if(l<=L&&R<=r)
    {
        a[root] += val;
        lazy[root] += val;
        return;
    }
    int M = (L + R) / 2;
    if(l<=M)
        Add(lchild(root), L, M, l, r, val);
    if(r>M)
        Add(rchild(root), M + 1, R, l, r, val);
    a[root] = max(a[lchild(root)], a[rchild(root)]);
    a[root] += lazy[root];
}
int Query(int root,int L,int R,int l,int r)
{
    if(l<=L&&R<=r)
        return a[root];
    int maxnum = -MAXN;
    PushDown(root, L, R);
    int M = (L + R) / 2;
    if(l<=M)
        maxnum = max(maxnum, Query(lchild(root), L, M, l, r));
    if(r>M)
        maxnum = max(maxnum, Query(rchild(root), M + 1, R, l, r));
    return maxnum;
}


int n;
int u1[MAXN],v1[MAXN],u2[MAXN],v2[MAXN];

int cnt[MAXN];
int curSet[MAXN],curL,curR;

int ans[MAXN];



int Lid[MAXN],Rid[MAXN];
int idcnt;
void dfsLRid(int X, int fX, vector<vector<int>> &G)
{
	Lid[X]=++idcnt;
	for (int o: G[X]) if (o!=fX) 
		dfsLRid(o,X,G);
	Rid[X]=idcnt;
}



void ADD(int X)
{
	Add(1, 0, n + 2, Lid[X], Rid[X], 1);
}


void DEL(int X)
{
	Add(1, 0, n + 2, Lid[X], Rid[X], -1);
}


bool fail(int X)
{
	return Query(1, 0, n + 2, Lid[X], Rid[X]) > 0;
}


void dfs(int X, int fX, vector<vector<int>> &GG)
{
	int lstL=curL,lstR=curR;
	
	while (curL<=curR && fail(X)) {
		DEL(curSet[curL]);
		curL++;
	}
	
	curR++;
	curSet[curR]=X;
	ADD(X);
	
	ans[X]=curR-curL+1;
	
	for (int o: GG[X]) if (o!=fX) {
		dfs(o,X,GG);
	}
	
	while (curL>lstL) {
		curL--;
		ADD(curSet[curL]);
	}
	DEL(X);
	curR=lstR;
}


void solve()
{
	scanf("%d",&n);
	vector<vector<int>> G1(n+1,vector<int>());
	vector<vector<int>> G2(n+1,vector<int>());
	for (int i=1; i<n; i++) scanf("%d %d",&u1[i],&v1[i]);
	for (int i=1; i<n; i++) scanf("%d %d",&u2[i],&v2[i]);
	
	for (int i=1; i<n; i++) {
		G1[u1[i]].push_back(v1[i]);
		G1[v1[i]].push_back(u1[i]);
	}
	
	for (int i=1; i<n; i++) {
		G2[u2[i]].push_back(v2[i]);
		G2[v2[i]].push_back(u2[i]);
	}
	
	
	idcnt=0;
	dfsLRid(1,0,G2);	//获得G2的dfs序,使得每个点的子树在序列中都是连续的一段区间 
	
	
	
	curL=1,curR=0;
	dfs(1,0,G1);		//遍历G1,处理答案 
	
	int ANS=0;
	for (int i=1; i<=n; i++) {
		ANS=max(ANS,ans[i]);
	}
	printf("%d\n",ANS);
}



int main()
{
	int ttt;
	scanf("%d",&ttt);
	while (ttt--) {

		solve();
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值