首页 > 其他分享 >CF519E A and B and Lecture Rooms(树上倍增 + 分类讨论)

CF519E A and B and Lecture Rooms(树上倍增 + 分类讨论)

时间:2024-06-21 13:11:38浏览次数:13  
标签:return fu int dep CF519E Lecture Rooms dist size

link

一眼看上去没什么思路,手摸一下样例,发现有不同性质的点对求解想法很不一样,考虑先分类讨论看看。

从简单的约束到强的约束分类讨论,这样更可做,也更好讨论,

比如首先我就想到两点是否重合,然后所求点一定要到两点的距离相等,我就想到路径长度的奇偶性,接着就考虑复杂的深度关系 ......

对于当前点对 (u,v),

  • 如果 u = v,则结果就是 n;

  • 如果 u ≠ v

    • 如果 u -> v 的简单路径长度为奇数,则结果为 0;

    • 如果 u -> v 的简单路径长度为偶数

      • 如果 \(dep[u] = dep[v]\),可以发现路径上到 lca 肯定是符合要求的中点 mid,再往以 lca 这颗子树往外走就步步重合了,也就是说都成立,如果 lca 子树中到 u、v 的两颗子树的根节点分别为 fu、fv,则有:

      \[size[1] - size[fu] - size[fv] \]

      • 如果 \(dep[u] \not = dep[v]\),假设令 \(dep[u] > dep[v]\),肯定还是要找路径上的中点 mid,但就一定不是 lca 了,但一定在较深的那颗子树里,所以还得从 u 往上跳一半的距离到 mid,显然满足的点肯定在 mid 延伸的子树上,且不是 u、v 所在的子树,这里只用考虑减掉 u 所在的子树大小就可以了,因为 v 肯定在 mid 往上走的路上,即:

        \[size[mid] - size[fu] \]

一开始实现后两部分讨论,我只用了普通的暴力上跳,\(O(mn)\) 的复杂度就 T 了(太投入了,我就老是会把想到脑边的做法先实现了再说)

code
#include <bits/stdc++.h>
#define re register int 

using namespace std;
const int N = 1e5 + 10, logN = 50; 

struct edge
{
	int to, next; 
}e[N << 1];
int top, h[N], dep[N], f[N][logN], size[N];
int n, m;

inline void add(int x, int y)
{
	e[++ top] = (edge){y, h[x]};
	h[x] = top;
}

void dfs(int u, int fa)
{
	dep[u] = dep[fa] + 1;
	
	f[u][0] = fa;
	for (re i = 1; i <= log2(n); i ++)
		f[u][i] = f[f[u][i - 1]][i - 1];
		
	for (re i = h[u]; i; i = e[i].next)
	{
		int v = e[i].to;
		
		if (v == fa) continue;
		dfs(v, u);
		
		size[u] += size[v];
	}
}

inline int lca(int u, int v)
{
	if (dep[u] < dep[v]) swap(u, v);
	
	for (re i = log2(n); i >= 0; i --)
		if (dep[f[u][i]] >= dep[v]) u = f[u][i];
		
	if (u == v) return u;
	
	for (re i = log2(n); i >= 0; i --)
		if (f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
		
	return f[u][0];
}

inline int work(int u, int v, int p)
{
	if (u == v) return n;
	
	int dist = dep[u] + dep[v] - 2 * dep[p];
	if (dist % 2) return 0;
	
	if (dep[u] == dep[v]) 
	{
//		return size[1] - size[p] + 1; 当 p = 1 时不成立
		
		int fu = u, fv = v, du, dv;
		 
		du = dv = dist / 2 - 1;
		while (du)
		{
			fu = f[fu][0];
			du --;	
		}  
		while (dv)
		{
			fv = f[fv][0];
			dv --;
		}
		
		return size[1] - size[fu] - size[fv];
	}
	else
	{
		if (dep[u] < dep[v]) swap(u, v);
		
		int fu = u, mid;
		dist = dist / 2 - 1;
		while (dist)
		{
			fu = f[fu][0];
			dist --;
		}
		mid = f[fu][0];
		
		return size[mid] - size[fu];
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n;
	for (re i = 1; i < n; i ++)
	{
		int x, y; cin >> x >> y;
		add(x, y); add(y, x);
	}
	for (re i = 1; i <= n; i ++) size[i] = 1;
	dfs(1, 0);
	
	cin >> m;
	while (m --)
	{
		int x, y; cin >> x >> y;
		
		cout << work(x, y, lca(x, y)) << '\n';
	}
	
	return 0;
}

所以显然是要倍增上跳就可以了。\(O(m\log n)\)

#include <bits/stdc++.h>
#define re register int 

using namespace std;
const int N = 1e5 + 10, logN = 50; 

struct edge
{
	int to, next; 
}e[N << 1];
int top, h[N], dep[N], f[N][logN], size[N];
int n, m;

inline void add(int x, int y)
{
	e[++ top] = (edge){y, h[x]};
	h[x] = top;
}

void dfs(int u, int fa)
{
	dep[u] = dep[fa] + 1;
	
	f[u][0] = fa;
	for (re i = 1; i <= log2(n); i ++)
		f[u][i] = f[f[u][i - 1]][i - 1];
		
	for (re i = h[u]; i; i = e[i].next)
	{
		int v = e[i].to;
		
		if (v == fa) continue;
		dfs(v, u);
		
		size[u] += size[v];
	}
}

inline int lca(int u, int v, int type)
{
	if (dep[u] < dep[v]) swap(u, v);
	
	for (re i = log2(n); i >= 0; i --)
		if (dep[f[u][i]] >= dep[v]) u = f[u][i];
		
	if (u == v) return u;
	
	for (re i = log2(n); i >= 0; i --)
		if (f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
		
	
	if (!type) return f[u][0];
	else return size[1] - size[u] - size[v];
}

inline int work(int u, int v, int p)
{
	if (u == v) return n;
	
	int dist = dep[u] + dep[v] - 2 * dep[p];
	if (dist % 2) return 0;
	
	if (dep[u] == dep[v]) 
	{
//		return size[1] - size[p] + 1; 当 p = 1 时不成立

		return lca(u, v, 1);
	}
	else
	{
		if (dep[u] < dep[v]) swap(u, v);
	
		int step = dist / 2;
		int cnt = size[u];
		
		for(int i = log2(n); i >= 0; i--)
			if(step - (1 << i) > 0)
			{
				u = f[u][i];
				cnt += size[u] - cnt;
				
				step -= (1 << i);
			}
		u = f[u][0];
		
		return size[u] - cnt;
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n;
	for (re i = 1; i < n; i ++)
	{
		int x, y; cin >> x >> y;
		add(x, y); add(y, x);
	}
	for (re i = 1; i <= n; i ++) size[i] = 1;
	dfs(1, 0);
	
	cin >> m;
	while (m --)
	{
		int x, y; cin >> x >> y;
		
		cout << work(x, y, lca(x, y, 0)) << '\n';
	}
	
	return 0;
}

标签:return,fu,int,dep,CF519E,Lecture,Rooms,dist,size
From: https://www.cnblogs.com/wenzieee/p/18260306

相关文章

  • CMU 15-445 Lecture #05: Storage Models & Compression笔记总结(上)
    这是cmu15-445第五节课程StorageModels&Compression的上半部分,主要包括StorageModels的内容,压缩部分下次再整理,学完这部分可以去做hw2的第一部分课程主页:CMU15-445/645::IntrotoDatabaseSystems(Fall2023)(有几张图片目前没上传,过两天补一下)DatabaseWorkloads......
  • qoj1138 Counting Mushrooms
    交互题。有一个隐藏的01序列\(a\),你只知道\(a\)的长度,并记为\(n\)。保证\(a_1=0\)。你可以执行以下操作:询问一个序列\(b\),满足两两不同且长度在\([2,1000]\)之间。交互库会返回\(\sum[a(b_i)\not=a(b_{i+1})]\)。请在\(226\)次操作内求出\(a\)中\(0\)......
  • CMU 15-751 CS Theory Toolkit Lecture Lecture 3 - Factorials & Binomial Coefficie
    CMU15-751课程第三课笔记。接上回CMU15-751-2。同样照抄参考了LectureNote。今天学习的是阶乘和二项式系数的渐进分析,这两种的出现频率非常高,因此我们很有必要熟悉他们的渐进方法。这也是我们做更多渐进分析的练习的机会。阶乘(Factorials)\(n!=2^{\Theta(n\logn)}\)......
  • CMU 15-751 CS Theory Toolkit Lecture 2 - Basic Asymptotics
    CMU15-751课程第二课笔记。CSTheoryToolkitatCMU-YouTube照抄参考了LectureNote。渐进标记(AsymptoticNotation)我们知道\[\sum_{i=1}^ni=\frac{n(n+1)}2=\frac12n^2+\frac12n\]在\(n\)很大的时候,平方项这个函数值的影响会更显著。我们可以把这一个特......
  • MIT6.S081 - Lecture3: OS Organization and System Calls
    为什么要使用操作系统使用操作系统的主要原因是为了实现CPU多进程分时复用以及内存隔离如果没有操作系统,应用程序会直接与硬件进行交互,这时应用程序会直接使用CPU,比如假设只有一个CPU核,一个应用程序在这个CPU核上运行,但是同时其他程序也需要运行,因为没有操作系统来帮助......
  • MIT6.S081 - Lecture1: Introduction and Examples
    课程简介课程目标理解操作系统的设计和实现通过XV6操作系统动手实验,可以扩展或改进操作系统操作系统的目标Abstraction:对硬件进行抽象Multiplex:在多个应用程序之间共用硬件资源Isolation:隔离性,程序出现故障时,不同程序之间不能相互干扰Sharing:实现共享,如数据交互或协......
  • Kirill and Mushrooms
    原题链接题解1.选k个数,就会有k-1个数没法选。我们可以倒着来,每少选一个数,就会多一个数可以选,添加总比删除简单2.最小的那个数,也就是第k小的数,因此我们可以维护一个大小为k的优先队列,最小值就是队首元素code#definelllonglong#include<bits/stdc++.h>usingnamespacest......
  • Lecture 11 Geometry 2 (Curves and Surfaces)
    Lecture11Geometry2(CurvesandSurfaces)Curves曲线BézierCurves贝塞尔曲线用一系列控制点定义摸一个曲线,这些控制点会定义曲线满足的一些性质图中通过三个控制点,可以定义曲线起始点和结束点一定在\(p_0\)和\(p_3\)上,并且起始的切线和结束的切线一定都是\(p_0p_1\)......
  • Lecture 09 Shading 3 (Texture Mapping cont
    Lecture09Shading3(TextureMappingcont.)Shading3Barycentriccoordinates重心坐标为了在三角形内部任何一点内插值,我们引入重心坐标为什么需要插值?指定顶点属性在三角形内部保持平滑变化插值什么内容?纹理坐标、颜色、法向量,...怎么做插值?重心坐标......
  • Lecture 10 Geometry 1 (Introduction)
    Lecture10Geometry1(Introduction)Examplesofgeometry几何的例子不同形状的几何光滑的曲面复杂的模型、位置摆放布料水滴城市(复杂在东西多)怎么存储怎么渲染这么大级别的东西离得远的情况下如何简化几何模型如何利用光线之间的连续性毛发微观几何树枝......