避难向导 树的直径 树上倍增 二分答案
来源:互联网 发布:淘宝店铺克隆软件 编辑:程序博客网 时间:2024/05/19 02:41
NKOJ3489【2015多校联训5】避难向导
问题描述
你良心受到了巨大的谴责,因此决定做出一些补救,回答一些逃难的人提出的询问。
已知该国一共有n 个城市,并且1 号城市是首都。(n-1)条双向的公路连接这些城市,通过这些公路,任意两个城市之间存在且仅存在一条路径。每条公路有一个长度。如果一个城市只与一条公路相连,则称它为边境城市。
该国政府有一个奇怪的规定:每个城市有一个封闭系数di,定义di 为离这个城市最远的边境城市到这个城市的距离。市民们认为,一个城市的安全系数Si 和它的封闭系数有很重要的联系。a,b,c 是该国的幸运数字,所以大家公认一个城市的安全系数Si = (di + a) * b mod c。
市民们一共会提出m 次询问。每个询问包含三个信息,xi,yi 和qi。xi 是询问者所在的城市编号。你要为这个询问者在xi 到yi 的必经之路上找出一个离xi最近的避难城市,并且要求这个避难城市的安全系数大于等于qi。如果存在这样的城市(包含xi 和yi),则输出城市编号,否则输出一行包括一个数-1。
输入格式
第一行五个数:依次是n, m, a, b, c。
接下来n-1 行描述公路的信息。每行三个数,前两个数代表这条公路连接的两个城市的编号,第三个数表示这条公路的长度。
再接下来m 行,每行描述一个询问,包含三个数xi, yi 和qi。
输出格式
对于每个询问,输出一行包含一个整数,存在符合要求的城市则输出城市编号,不存在则输出-1。
样例输入
7 6 5 6 20
1 2 4
2 4 2
2 5 3
1 3 5
3 6 6
6 7 7
7 5 15
3 4 5
5 4 2
4 5 2
6 6 10
3 5 19
样例输出
6
3
2
4
6
-1
数据范围
对于100%数据, 0< xi, yi<=n<=100000; a,b,c,qi<=1,000,000;
注意:计算安全系数的中间过程可能需要使用64 位整数。
首先要解决离某个城市最远的城市的距离。树形DP当然可以搞,但是这在主要问题之前就给自己增加了难度。实际上,离任意一个城市最远的点一定是直径的端点。这个采用反证法不难证明。找树的直径不就是用的这个性质吗?
几遍DFS就可以方便地求出
既然是与路径相关,肯定要考虑LCA。
首先考虑判无解。求出一段路径中点权的最值,可以使用倍增法,注意代码中的
在考虑有解的情况下怎样找到离起点最近且权值满足要求的点。这样的点无非有两种情况:在起点到LCA的路径上或是在LCA到终点的路径上。分别考虑如何解决:
在起点到LCA的路径上:这样的点要求深度尽量深。借鉴倍增法求LCA的思想即可:从大到小枚举
i ,看向上跳2i 是否满足要求,不满足就向上跳。复杂度O(logn) 。在LCA到终点的路径上:这样的情况相对更难搞,因为要求的是深度尽可能浅。由于从LCA到终点的路径上的最值单调不减,可以用二分答案讨论改点到终点的距离,向上跳之后倍增验证即可,复杂度
O(log2n)
所以,处理询问时,先讨论是否有解;若有解,则先讨论第一种情况,不满足则讨论第二种情况。最坏情况下时间复杂度
本题涉及知识较多,虽然难度不大,但代码量比较大,细节的处理比较多。考试时过不了,主要是忘记了这个二分答案模板求最大值输出R,最小值输出L。由于是二分的向上跳的距离,所以要求尽可能大,取R才对。
代码:
#include<stdio.h>#include<algorithm>#define MAXN 300005#define MAXM 600005#define ll long longusing namespace std;int N,M;int en[MAXM],nex[MAXM],las[MAXN],tot;ll len[MAXM];void Add(int x,int y,ll z) { en[++tot]=y; nex[tot]=las[x]; las[x]=tot; len[tot]=z;}int ID,St,En;ll MaxDis;void dfsd(int x,int f,ll dis) { int i,y; if(dis>MaxDis)MaxDis=dis,ID=x; for(i=las[x]; i; i=nex[i]) { y=en[i]; if(y==f)continue; dfsd(y,x,dis+len[i]); }}void FindD() {//找直径的端点 MaxDis=0; dfsd(1,0,0); St=ID; MaxDis=0; dfsd(ID,0,0); En=ID;}ll S[MAXN],D[MAXN];void dfss(int x,int f,ll dis) { int i,y; if(D[x]<dis)D[x]=dis; for(i=las[x]; i; i=nex[i]) { y=en[i]; if(y==f)continue; dfss(y,x,dis+len[i]); }}void GetS(ll A,ll B,ll C) { dfss(St,0,0); dfss(En,0,0); int i; for(i=1; i<=N; i++)S[i]=(D[i]+A)%C*B%C;}int dep[MAXN],fa[MAXN][20];ll V[MAXN][20];void DFS(int x,int f) { int i,y; dep[x]=dep[f]+1; fa[x][0]=f; V[x][0]=S[f]; for(i=1; i<20; i++) { fa[x][i]=fa[fa[x][i-1]][i-1]; V[x][i]=max(V[x][i-1],V[fa[x][i-1]][i-1]); } for(i=las[x]; i; i=nex[i]) { y=en[i]; if(y==f)continue; DFS(y,x); }}ll MaxV;int LCA(int x,int y) { int i,t,d; MaxV=max(S[x],S[y]); if(dep[x]<dep[y])t=x,x=y,y=t; d=dep[x]-dep[y]; for(i=0; i<20; i++)if((d>>i)&1)MaxV=max(MaxV,V[x][i]),x=fa[x][i]; if(x==y)return x; for(i=19; i>=0; i--) if(fa[x][i]!=fa[y][i]) { MaxV=max(MaxV,V[x][i]); MaxV=max(MaxV,V[y][i]); x=fa[x][i]; y=fa[y][i]; } MaxV=max(MaxV,V[x][0]); MaxV=max(MaxV,V[y][0]); return fa[x][0];}int Up(int x,int lca,ll k) {//情况一 int i; for(i=19; i>=0; i--)if(V[x][i]<k)x=fa[x][i]; if(dep[fa[x][0]]<dep[lca])return -1; return fa[x][0];}int Down(int y,int lca,ll k) {//情况二 int i,d,L,R,mid,t; ll tmpv; d=dep[y]-dep[lca]; L=0; R=d; while(L<=R) {//二分从终点向上跳的距离 mid=L+R>>1; t=y; for(i=0; i<20; i++)if((mid>>i)&1)t=fa[t][i]; tmpv=S[t]; for(i=0; i<20; i++)if(((d-mid)>>i)&1)tmpv=max(tmpv,V[t][i]),t=fa[t][i]; if(tmpv>=k)L=mid+1; else R=mid-1; } for(i=0; i<20; i++)if((R>>i)&1)y=fa[y][i]; return y;}int main() { int i,x,y,lca,ans; ll A,B,C,z; scanf("%d%d%lld%lld%lld",&N,&M,&A,&B,&C); for(i=1; i<N; i++) { scanf("%d%d%lld",&x,&y,&z); Add(x,y,z); Add(y,x,z); } FindD(); GetS(A,B,C); DFS(1,0); while(M--) { scanf("%d%d%lld",&x,&y,&z); if(S[x]>=z) { printf("%d\n",x); continue; } lca=LCA(x,y); if(MaxV<z) { puts("-1"); continue; } ans=Up(x,lca,z); if(ans!=-1)printf("%d\n",ans); else ans=Down(y,lca,z),printf("%d\n",ans); }}
- 避难向导 树的直径 树上倍增 二分答案
- 树上方法总结 LCA 树上倍增 树链剖分 树的直径 重心
- BZOJ4556:[Tjoi2016&Heoi2016]字符串 (后缀自动机+树上倍增+二分答案+线段树合并)
- 【NOIP2015】运输计划 {二分答案+倍增+树上差分}
- ural1752找树上距某个点某距离的点(树的直径+倍增)
- NKOJ 3489 避难向导(LCA+倍增+DFS/DP)
- 避难向导
- 2282: [Sdoi2011]消防 树的直径+二分答案
- 【树DP+LCA】[CQBZOJ2937]避难向导
- 【运输计划】【二分】【树上倍增】
- 3306: 树 树上倍增
- NKOJ 2650 (SDOI 2011) 消防(树的直径+DP+单调队列/二分答案)
- ural 1750 Tree 2(树的直径 + 倍增)
- URAL 1752 Tree 2 树的直径与倍增
- 树的直径 (树上的最长路)
- hdu 2196 Computer(树的直径\树上dp)
- Codevs 2370 小机房的树 LCA 树上倍增
- 树上倍增
- web测试方法总结
- 选课
- 图片手风琴效果
- eclipse下maven打jar包并引入依赖包
- mfc接收udp组播264视频流
- 避难向导 树的直径 树上倍增 二分答案
- centos7.2 sonarqube代码质量管理平台的安装与使用
- [NOIP模拟] 路径统计 floyd
- java中复写方法和同名变量的调用问题
- js基础——运算符
- 查询只选修了一门课程的学生
- 栈
- GStreamer学习笔记
- C#/.NET整数的三种强制类型转换(int)、Convert.ToInt32()、int.Parse()的区别