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

来源:互联网 发布:淘宝好评返现怎么弄 编辑:程序博客网 时间:2024/06/08 15:21
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 1400003using 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