[CSP-S模拟测试]:Dash Speed(线段树+并查集+LCA)

题目描述

  比特山是比特镇的飙车圣地。在比特山上一共有$n$个广场,编号依次为$1$到$n$,这些广场之间通过$n−1$条双向车道直接或间接地连接在一起,形成了一棵树的结构。
  因为每条车道的修建时间以及建筑材料都不尽相同,所以可以用两个数字$l_i,r_i$量化地表示一条车道的承受区间,只有当汽车以不小于$l_i$且不大于$r_i$的速度经过这条车道时,才不会对路面造成伤害。
  $Byteasar$最近新买了一辆跑车,他想在比特山飙一次车。$Byteasar$计划选择两个不同的点$S,T$,然后在它们树上的最短路径上行驶,且不对上面任意一条车道造成伤害。
  $Byteasar$不喜欢改变速度,所以他会告诉你他的车速。为了挑选出最合适的车速,$Byteasar$一共会向你询问$m$次。请帮助他找到一条合法的道路,使得路径上经过的车道数尽可能多。


输入格式

  第一行包含两个正整数$n,m$,表示广场的总数和询问的总数。
  接下来$n−1$行,每行四个正整数$u_i,v_i,l_i,r_i$,表示一条连接$u_i$和$v_i$的双向车道,且承受区间为$[l_i,r_i]$。
  接下来$m$行,每行一个正整数$q_i$,分别表示每个询问的车速。


输出格式

  输出$m$行,每行一个整数,其中第$i$行输出车速为$q_i$时的最长路径的长度,如果找不到合法的路径则输出$0$。


样例

我们将速度$l,r$看成区间,那么我们就是要找速度是$q_i$时所能经过多少连续区间。

现将问题离线。

那么可以用线段树维护边,对于线段树上区间$l,r$,我们将边的这条边压进去。

然后我们在线段树上往上合并每一条边,用并查集维护是否已经连到了一起,合并时一共分为$6$种情况;注意并查集不能路径压缩,要按秩合并,因为我们在合并的同时还要维护信息。

对于合并时求距离,还是用$LCA$,但是倍增$LCA$常数较大,建议用树链剖分。

时间复杂度:$\Theta(n\log^2n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
struct rec{int nxt,to;}e[150000];
struct node{int x,y,v;pair<int,int> p;}sta[70001];
int head[70001],cnt;
int n,m,t;
int f[70001],dep[70001];
pair<int,int> st[70001];
int fa[70001],depth[70001],top[70001],size[70001],son[70001];
vector<pair<int,int> > pos[500000];
int ans[70001];
void add(int x,int y)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
int find(int x){return x==f[x]?x:find(f[x]);}
void dfs1(int x)
{
	size[x]=1;
	for(int i=head[x];i;i=e[i].nxt)
		if(e[i].to!=fa[x])
		{
			fa[e[i].to]=x;
			depth[e[i].to]=depth[x]+1;
			dfs1(e[i].to);
			size[x]+=size[e[i].to];
			if(size[e[i].to]>size[son[x]])son[x]=e[i].to;
		}
}
void dfs2(int x,int tp)
{
	top[x]=tp;
	if(!son[x])return;
	dfs2(son[x],tp);
	for(int i=head[x];i;i=e[i].nxt)
		if(!top[e[i].to])dfs2(e[i].to,e[i].to);
}
int LCA(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(depth[top[x]]<depth[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	return depth[x]<depth[y]?x:y;
}
int dis(int x,int y){return depth[x]+depth[y]-(depth[LCA(x,y)]<<1);}
void build(int x,int l,int r,int L,int R,int u,int v)
{
	if(r<L||R<l)return;
	if(L<=l&&r<=R){pos[x].push_back(make_pair(u,v));return;}
	int mid=(l+r)>>1;
	build(L(x),l,mid,L,R,u,v);
	build(R(x),mid+1,r,L,R,u,v);
}
void merge(int x,int y,int &flag)
{
	x=find(x);
	y=find(y);
	int res=0,d;
	pair<int,int> mzz;
	if(res<(d=dis(st[x].first,st[x].second)))res=d,mzz=make_pair(st[x].first,st[x].second);
	if(res<(d=dis(st[x].first,st[y].first)))res=d,mzz=make_pair(st[x].first,st[y].first);
	if(res<(d=dis(st[x].first,st[y].second)))res=d,mzz=make_pair(st[x].first,st[y].second);
	if(res<(d=dis(st[x].second,st[y].first)))res=d,mzz=make_pair(st[x].second,st[y].first);
	if(res<(d=dis(st[x].second,st[y].second)))res=d,mzz=make_pair(st[x].second,st[y].second);
	if(res<(d=dis(st[y].first,st[y].second)))res=d,mzz=make_pair(st[y].first,st[y].second);
	flag=max(flag,res);
	if(dep[x]<dep[y])swap(x,y);
	sta[++t]=(node){x,y,0,st[x]};
	if(dep[x]==dep[y])
	{
		dep[x]++;
		sta[t].v=1;
	}
	f[y]=x;
	st[x]=mzz;
}
void del(int x)
{
	while(x<t)
	{
		dep[sta[t].x]-=sta[t].v;
		f[sta[t].y]=sta[t].y;
		st[sta[t].x]=sta[t].p;
		t--;
	}
}
void solve(int x,int l,int r,int res)
{
	int now=t;
	for(int i=0;i<pos[x].size();i++)
		merge(pos[x][i].first,pos[x][i].second,res);
	if(l==r){ans[l]=res;del(now);return;}
	int mid=(l+r)>>1;
	solve(L(x),l,mid,res);
	solve(R(x),mid+1,r,res);
	del(now);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++)
	{
		int u,v,l,r;
		scanf("%d%d%d%d",&u,&v,&l,&r);
		add(u,v);add(v,u);
		build(1,1,n,l,r,u,v);
	}
	dfs1(1);
	dfs2(1,1);
	for(int i=1;i<=n;i++)
	{
		f[i]=i;
		st[i]=make_pair(i,i);
	}
	solve(1,1,n,0);
	while(m--)
	{
		int x;scanf("%d",&x);
		printf("%d\n",ans[x]);
	}
	return 0;
}


rp++

原文地址:https://www.cnblogs.com/wzc521/p/11623020.html

时间: 2024-10-13 00:30:14

[CSP-S模拟测试]:Dash Speed(线段树+并查集+LCA)的相关文章

UVALive 4730 Kingdom 线段树+并查集

题目链接:点击打开链接 题意见白书P248 思路: 先把读入的y值都扩大2倍变成整数 然后离散化一下 用线段树来维护y轴 区间上每个点的 城市数量和联通块数量, 然后用并查集维护每个联通块及联通块的最大最小y值,还要加并查集的秩来记录每个联通块的点数 然后就是模拟搞.. T^T绝杀失败题..似乎数组开小了一点就过了,== #include<stdio.h> #include<math.h> #include<vector> #include<string.h>

UVA 1455 - Kingdom(线段树+并查集)

UVA 1455 - Kingdom 题目链接 题意:给定一些城市坐标点,连在一起的城市称为一个州,现在用两种操作,road表示把城市a,b建一条路,line表示询问一个y轴上穿过多少个州,和这些州共包含多少个城市 思路:利用并查集维护每个州的上界和下界还有城市个数,然后每次加进一条路的时候,根据两个集合的位置可以处理出区间的州和城市数如何进行加减,然后利用线段树搞就可以了 代码: #include <cstdio> #include <cstring> #include <

[CSP-S模拟测试]:Permutation(线段树+拓扑排序+贪心)

题目描述 你有一个长度为$n$的排列$P$与一个正整数$K$你可以进行如下操作若干次使得排列的字典序尽量小对于两个满足$|i−j|\geqslant K$且$|P_i−P_j|=1$的下标$i$与$j$,交换$P_i$与$P_j$ 输入格式 第一行包括两个正整数$n$与$K$第二行包括$n$个正整数,第$i$个正整数表示$P_i$ 输出格式 输出一个新排列表示答案输出共$n$行,第$i$行表示$P_i$ 样例 样例输入: 8 34 5 7 8 3 1 2 6 样例输出: 12675348 数据范

(线段树+并查集) Codeforces Round #416 (Div. 2) E Vladik and Entertaining Flags

In his spare time Vladik estimates beauty of the flags. Every flag could be represented as the matrix n?×?m which consists of positive integers. Let's define the beauty of the flag as number of components in its matrix. We call component a set of cel

【BZOJ-3673&amp;3674】可持久化并查集 可持久化线段树 + 并查集

3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1878  Solved: 846[Submit][Status][Discuss] Description n个集合 m个操作操作:1 a b 合并a,b所在集合2 k 回到第k次操作之后的状态(查询算作操作)3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 0<n,m<=2*10^4 Input Output Sample Input 5 6

【BZOJ 4662】 4662: Snow (线段树+并查集)

4662: Snow Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 136  Solved: 47 Description 2333年的某一天,临冬突降大雪,主干道已经被雪覆盖不能使用.城 主 囧·雪 决定要对主干道进行一次清扫. 临冬城的主干道可以看为一条数轴.囧·雪 一共找来了n个清理工,第 i个清理工的工作范围为[li,ri],也就是说这个清理工会把[li,ri]这一 段主干道清理干净(当然已经被清理过的部分就被忽略了).当然有可能主 干道

UVALive 4730 线段树+并查集

点击打开链接 题意:在坐标上给n个点,r的操作是将两个点连起来,l的操作是问你y=u的这条线连接的集合块数和这些集合内的点的个数 思路:很麻烦的一道题,在网上看了题意和做法后,开始了一下午的调bug过程,做法很好懂,我开了两个线段树,一个维护点代表的直线的集合个数,另一个则是路过集合内的点的个数,然后集合的判断直接用并查集就行了,这是两个核心,然后就是自己瞎写的了,代码丑的可以而且好像除了本人别人看着可能要骂人了,有兴趣研究的可以留言我来解答,那难的部分其实就是并查集合并时该怎么将这两个要维护的

【Codeforces811E】Vladik and Entertaining Flags [线段树][并查集]

Vladik and Entertaining Flags Time Limit: 20 Sec  Memory Limit: 512 MB Description n * m的矩形,每个格子上有一个数字代表颜色. q次询问,询问[l, r]有几个连通块,若颜色相同并且连通则属于同一个连通块. Input 输入第一行n,m,q. 然后一个n*m的矩形. 之后q行,每行两个整数l,r. Output 输出q行,对于每个询问输出答案. Sample Input 4 5 4 1 1 1 1 1 1 2

【XSY2707】snow 线段树 并查集

题目描述 有\(n\)个人和一条长度为\(t\)的线段,每个人还有一个工作范围(是一个区间).最开始整条线段都是白的.定义每个人的工作长度是这个人的工作范围中白色部分的长度(会随着线段改变而改变).每一天开始时你要选择一个人满足这个人的工作长度最小(如果有多个就选编号最小的).把这个人的工作区间染黑.请你输出每天你选了哪个人. 保证工作范围中左端点和右端点单调递增. \(n\leq 300000\) 题解 先把线段离散化成很多个小区间,那么每个小区间只会被染黑一次(染黑之后不会变白). 因此每次