首页 > 其他分享 >根号分治(待补充)

根号分治(待补充)

时间:2024-08-05 19:27:30浏览次数:8  
标签:dep vis 补充 分治 st int now 根号

最近有根号分治的题都没那么熟悉,想到了方向感也很差,故写一点题解。

LCA查询

看到题目中的条件 \(\sum k \le 10^5\)

说明 \(k \le S\) 的个数很多,\(S \le k\) 的个数很少。

那么对于前者,考虑一开始最朴素的 \(O(S^2)\) 的暴力,枚举集合中的两个点,求 \(LCA\) 总时间复杂度 \(O( \frac{N}{S} \cdot S^2 \cdot log N)\)。

对于后者,若 \(n^2\) 肯定不行。考虑到我们的限制:一共也就 \(n\) 个点,先让 \(A\) 集合里的点向上跳,将点都标起来,若跳到该点已经走过就不用再跑,这样保证了每个点只会被遍历 \(1\) 次。再让 \(B\) 里的点向上跳,若碰到 \(A\) 的标记,进行深度统计,不再向上(应为上面不可能更优)。时间复杂度 \(O(\frac{N}{S} \cdot N)\)

跑起来挺快。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n,m,dep[N];
int siza,a[N];
int sizb,b[N];
int st[N][18];
vector<int> e[N];
void dfs(int u,int fa){
	dep[u] = dep[fa] + 1;
	st[u][0] = fa;
	for(int i = 1;i<=17;++i)st[u][i] = st[st[u][i-1]][i-1];
	for(int v : e[u])if(v ^ fa)dfs(v,u);
}
int LCA(int x,int y){
	if(dep[x] > dep[y])swap(x,y);
	int t = dep[y] - dep[x];
	for(int i = 17;~i;--i)if(t&(1<<i))y = st[y][i];
	if(x == y)return x;
	for(int i = 17;~i;--i)if(st[x][i] ^ st[y][i])
		x = st[x][i], y = st[y][i];
	return st[x][0];
}
int vis[N];
int main(){
	scanf("%d%d",&n,&m);
	for(int i = 1,u,v;i<n;++i){
		scanf("%d%d",&u,&v);
		e[u].push_back(v),e[v].push_back(u);
	}
	dfs(1,0);
	
	while(m--){
		scanf("%d",&siza); for(int i = 1;i<=siza;++i)scanf("%d",&a[i]);
		scanf("%d",&sizb); for(int i = 1;i<=sizb;++i)scanf("%d",&b[i]);
		
		int ans = 0;
		if(siza <= 100 && sizb <= 100){
			for(int i = 1;i<=siza;++i)
				for(int j = 1;j<=sizb;++j)
					ans = max(ans,dep[LCA(a[i],b[j])]);
			printf("%d\n",ans);
		}else{
			for(int i = 1;i<=siza;++i){
				int now = a[i];
				while(now){
					if(vis[now] > 0)break;
					vis[now] = 1;
					now = st[now][0];
				}
			}
			for(int i = 1;i<=sizb;++i){
				int now = b[i];
				while(now){
					if(vis[now] > 0){
						if(vis[now] == 1)ans = max(ans,dep[now]);
						break;
					}
					vis[now] = 2;
					now = st[now][0];
				}
			}
			memset(vis,0,sizeof (int)*(n+5));
			printf("%d\n",ans);
		}
	}
	return 0;
}

CF1997E Level Up

标签:dep,vis,补充,分治,st,int,now,根号
From: https://www.cnblogs.com/fight-for-humanity/p/18343914

相关文章

  • 「Day 2—贪心问题&分治&前缀和」
    贪心问题定义顾名思义,越贪越好。。。习题P1094[NOIP2007普及组]纪念品分组思路简单来说:最少的+最多的,利用双指针。代码#include<algorithm>#include<iostream>usingnamespacestd;intw,n;intp[30005];intmain(){cin>>w;cin>>n;for(inti=1;i......
  • HCIE学习笔记(持续补充更新):OSPF 五种报文、LSA
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、OSPF基础1、OSPF三张表2、OSPF建立邻接关系的过程2.1建立邻居关系2.2主/从关系协商、DD报文交换2.3、LSDB同步(LSA请求、LSA传输、LSA应答)二、OSPF报文OSPF报头1、OSPFHello报文(选DR、B......
  • 补充:关于GRU的详细运作原理以及特殊的优化思路
    1.GRU的基本结构和运作原理1.1GRU的基本概念GatedRecurrentUnit(GRU)是一种简化版的循环神经网络(RNN),它通过引入门控机制来解决长期依赖问题,同时减少参数数量以降低计算复杂度。1.2GRU的结构详解GRU包含两个门控机制:更新门(updategate)和重置门(resetgat......
  • 边分治维护强连通分量(CF1989F,P5163)
    这里的边分治和树上的点分治边分治不一样,是维护强连通分量用的,每条边有一个出现时间,通过将每条边按连通关系分流重新排列,从而维护每个时间点整张图的连通性。具体的,这个算法是维护这样的一类问题:n个点,m条边按时间顺序依次加入,每加入一条边,你需要回答一些问题,比如在这个时间点......
  • 树分治
    点分治点分治适合处理大规模的树上路径信息问题。LuoguP4178Tree给定一棵有\(n\)个点的带权树,给出\(k\),询问树上距离小于等于\(k\)的点对数量。分治地考虑每一个子树,求出重心,对答案有贡献的是三种点对,依次解决。重心-子树上的点直接累加贡献即可。子树上的点......
  • 得物web端逆向 补环境(第二部分补充已出)
    ​声明:本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!wxa15018601872       本文章未经许可禁止转载,禁止任何修改后二次传播,......
  • cdq分治
    cdq分治主要思想为分治,分为三个部分:左区间内部。左区间对右区间。右区间内部。一个保险的标准顺序是先处理左区间,再处理左区间对右区间的贡献,最后处理右区间,这样就可以保证时序性了。注意这种写法在处理左区间对右区间贡献是要先按标号排序分出正确的左右区间,如果是先递归......
  • 解密编程的八大法宝(四)(附二分查找、分治法和图论算法(深度和广度优先搜索、最短路径、最
    算法题中常见的几大解题方法有以下几种::暴力枚举法(BruteForce):这是最基本的解题方法,直接尝试所有可能的组合或排列来找到答案。这种方法适用于问题规模较小的情况,但在大多数情况下效率不高。贪心算法(GreedyAlgorithm):贪心算法在每一步都选择当前看起来最优的解,希望最终能......
  • 点分治板子
    #define_CRT_SECURE_NO_WARNINGS#include<iostream>#include<vector>#include<algorithm>#include<math.h>#include<sstream>#include<string>#include<string.h>#include<iomanip>#include<stdlib.h>#inc......
  • 线段树分治小结
    一般来说,这种题目是给定一些信息,比如说边,然后这些信息会在一个时间段内出现。一般来说需要离线,然后建立一棵以维护每个时刻信息的线段树,具体来说就是在每个节点维护一个vector。#121.「离线可过」动态图连通性以经典例题来说,我们根据每条边出现的时刻将它插入到线段树上对应时......