test2 Problem C. Dash Speed (并查集+分治)

Problem C. Dash Speed(speed.c/cpp/pas)
Time limit: 1 seconds
Memory limit: 128 megabytes
比特山是比特镇的飙车圣地。在比特山上一共有 n 个广场,编号依次为 1 到 n,这些广场之间通过
n   1 条双向车道直接或间接地连接在一起,形成了一棵树的结构。
因为每条车道的修建时间以及建筑材料都不尽相同,所以可以用两个数字 li; ri 量化地表示一条车道
的承受区间,只有当汽车以不小于 li 且不大于 ri 的速度经过这条车道时,才不会对路面造成伤害。
Byteasar 最近新买了一辆跑车,他想在比特山飙一次车。Byteasar 计划选择两个不同的点 S; T,然
后在它们树上的最短路径上行驶,且不对上面任意一条车道造成伤害。
Byteasar 不喜欢改变速度,所以他会告诉你他的车速。为了挑选出最合适的车速,Byteasar 一共会
向你询问 m 次。请帮助他找到一条合法的道路,使得路径上经过的车道数尽可能多。
Input
第一行包含两个正整数 n;m,表示广场的总数和询问的总数。
接下来 n   1 行,每行四个正整数 ui; vi; li; ri,表示一条连接 ui 和 vi 的双向车道,且承受区间为
[li; ri]。
接下来 m 行,每行一个正整数 qi,分别表示每个询问的车速。
Output
输出 m 行,每行一个整数,其中第 i 行输出车速为 qi 时的最长路径的长度,如果找不到合法的路
径则输出 0。
Examples
speed.in
5 3
3 2 2 4
1 5 2 5
4 5 2 2
1 2 3 5
1
2

3

speed.out

0
2
3
当车速为 1 时,不存在合法的路径。
当车速为 2 时,可以选择 1-5-4 这条路径,长度为 2。

当车速为 3 时,可以选择 3-2-1-5 这条路径,长度为 3。



题解:并查集+分治。

先说一下3,4个点。

 将所有边按 r 从大到小排序后依次加入,对于一个询问保证所有r>=ask的边已经加入,那么当前的最长链就是最长区间的长度。并查集维护即可。时间复杂度 O(n log n)。

5,6点,依然是树链。

我们将每条边拆成两个事件,因为l,r在[1..n]之间,所有我们可以枚举1到n。

当i=l时,将树链中当前位置的点染黑; 当i=r+1的时候,将树链中当前位置的点染白。此时对应的答案就是是树链中最长连续黑点的区间长度,这个的话可以用线段树维护。

7,8点,没有下界。

将所有边按 r 从大到小排序后依次加入,需要维护这个森林的最长链。
对于每个连通块维护其最长链,那么合并两个连通块的时候,新的最长链一定来自于那 4 个点(两个联通块最长链的起终点),分 6 种情况取最优解即可。并查集维护。
时间复杂度 O(n log n)。

对于所有的数据我们需要用到分治。

我们考虑如何计算速度为v的答案,显然就是将所有范围包含v的边都加入,然后计算此时树中的最长链。我们考虑分治对于[l,r]区间,将所有范围覆盖[l,r]区间的边加入树中,剩下的边递归进入下一层在处理。如果当前区间只包含一个数l,那么此时的最长链就是速度为l时的答案。

但是我们需要回溯,就是将不符合当前区间的答案删除影响,所以我们不能讲并查集进行路径压缩,我们可以按秩合并(所谓按秩合并,就是将小的合并到大的中),然后我们将没合并前的状态记录下来,处理完当前区间再将其还原。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 700010
#define M 1400003
using namespace std;
int n,m,fa[N],deep[N],size[N],f[N][20],sz;
int point[N],next[N*2],c[N*2],tot;
int head[M],nxt[M],u[N],v[N],cnt,ans[N];
int a[M],b[M],mi[20];
struct data
{
	int opt,x,y;
}e[M];
int find(int x)
{
	return fa[x]==x?x:find(fa[x]);
}
void add(int x,int y)
{
	tot++; next[tot]=point[x]; point[x]=tot; c[tot]=y;
	tot++; next[tot]=point[y]; point[y]=tot; c[tot]=x;
}
void insert(int now,int l,int r,int ll,int rr,int x,int y)//用类似线段树的方式处理出保护该区间的边的集合
{
	if (ll<=l&&r<=rr)
	 {
	 	cnt++; nxt[cnt]=head[now]; head[now]=cnt;
	 	u[cnt]=x; v[cnt]=y;
	 	return;
	 }
	int mid=(l+r)/2;
	if (ll<=mid) insert(now<<1,l,mid,ll,rr,x,y);
	if (rr>mid) insert(now<<1|1,mid+1,r,ll,rr,x,y);
}
void dfs(int x,int f1)
{
	deep[x]=deep[f1]+1;
	for (int i=1;i<=17;i++)
	 {
	 	if (deep[x]-mi[i]<0) break;
	 	f[x][i]=f[f[x][i-1]][i-1];
	 }
	for (int i=point[x];i;i=next[i])
	 if (c[i]!=f1)
	  {
	  	f[c[i]][0]=x;
	  	dfs(c[i],x);
	  }
}
int lca(int x,int y)
{
	if (deep[x]<deep[y])  swap(x,y);
	int k=deep[x]-deep[y];
	for (int i=0;i<=17;i++)
	 if (k>>i&1) x=f[x][i];
	if (x==y) return x;
	for (int i=17;i>=0;i--)
	 if (f[x][i]!=f[y][i])
	  x=f[x][i],y=f[y][i];
	return f[x][0];
}
void solve(int x,int y,int &t,int &u,int &v)
{
	//cout<<x<<" "<<y<<" "<<lca(x,y)<<endl;
	int d=deep[x]+deep[y]-2*deep[lca(x,y)];
	if (d>t)
	 t=d,u=x,v=y;
}
void add1(int t,int x,int y)
{
	sz++; e[sz].opt=t; e[sz].x=x; e[sz].y=y;
}
void merge(int x,int y,int &len)
{
	x=find(x); y=find(y);
	int u,v,t=0;
	solve(a[x],a[y],t,u,v);  solve(b[x],b[y],t,u,v);
	solve(a[x],b[x],t,u,v);  solve(b[x],a[y],t,u,v);
	solve(a[x],b[y],t,u,v);  solve(b[y],a[y],t,u,v);
    len=max(len,t);
    if (size[x]==size[y]){
    	size[x]++; 
    	add1(0,x,0);
	}
	if (size[x]<size[y]) swap(x,y);
	add1(1,y,0);
	add1(2,x,a[x]); add1(3,x,b[x]);
	fa[y]=x; a[x]=u; b[x]=v;
}
void rebuild(int x)
{
	while (sz>x)
	 {
	 	if (e[sz].opt==0)  size[e[sz].x]--;
	 	if (e[sz].opt==1)  fa[e[sz].x]=e[sz].x;
	 	if (e[sz].opt==2)  a[e[sz].x]=e[sz].y;
	 	if (e[sz].opt==3)  b[e[sz].x]=e[sz].y;
	 	sz--;
	 }
}
void solve(int now,int l,int r,int len)
{
	int pos=sz; 
	for(int i=head[now];i;i=nxt[i])
	 merge(u[i],v[i],len);
	if (l==r)
	 {
	 	ans[l]=len;
	 	rebuild(pos);
	 	return;
	 }
	int mid=(l+r)/2;
	solve(now<<1,l,mid,len);
	solve(now<<1|1,mid+1,r,len);
	rebuild(pos);
}
int main()
{
	freopen("speed.in","r",stdin);
	scanf("%d%d",&n,&m);
	for (int i=1;i<n;i++)
	 {
	 	int x,y,l,r; scanf("%d%d%d%d",&x,&y,&l,&r);
	 	add(x,y); insert(1,1,n,l,r,x,y);
	 }
	mi[0]=1;
	for (int i=1;i<=19;i++) mi[i]=mi[i-1]*2;
	dfs(1,0);
	for (int i=1;i<=n;i++) fa[i]=a[i]=b[i]=i;
	solve(1,1,n,0);
	for (int i=1;i<=m;i++)
	{
		int x; scanf("%d",&x);
		printf("%d\n",ans[x]);
	}
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值