[BZOJ2791]:[Poi2012]Rendezvous(塔尖+倍增LCA)
题目传送门
题目描述
给定一个有n个顶点的有向图,每个顶点有且仅有一条出边。每次询问给出两个顶点${a}_{i}$和${b}_{i}$,求满足以下条件的${x}_{i}$和${y}_{i}$:
• 从顶点${a}_{i}$沿出边走${x}_{i}$步与从顶点${b}_{i}$沿出边走${y}_{i}$步到达的顶点相同。
• $max({{x}_{i}},{{y}_{i}})$最小。
• 满足以上条件的情况下$min({{x}_{i}},{{y}_{i}})$最小。
• 如果以上条件没有给出一个唯一的解,则还需要满足${x}_{i}$≥${y}_{i}$。
如果不存在这样的${x}_{i}$和${y}_{i}$,则${x}_{i}$=${y}_{i}$=−1。
输入格式
第一行两个正整数n和k,表示顶点数和询问个数。
接下来一行n个正整数,第i个数表示i号顶点出边指向的顶点。
接下来k行表示询问,每行两个整数${a}_{i}$和${b}_{i}$。
输出格式
对每组询问输出两个整数${x}_{i}$和${y}_{i}$。
样例
样例输入:
12 5
4 3 5 5 1 1 12 12 9 9 7 1
7 2
8 11
1 2
9 10
10 5
样例输出:
2 3
1 2
2 2
0 1
-1 -1
数据范围与提示
对于40%的数据,n≤2000,k≤2000。
对于100%的数据,1≤n≤500,000,1≤k≤500,000。
题解
正解显然是基环树,但是我还用不六,完了……
我觉得你可能觉得塔尖和倍增LCA怎么着也不会卡在一起,但是我做到了。
其实,我只会用塔尖缩点和用倍增求LCA,于是便诞生了这个塔尖和倍增LCA结合在一起的代码。
然后我发现网上并没有这种解法,所以,推荐你不要直接颓代码,不然容易被教练干……
言归正转,首先,这张图里肯定有且只有一个环,那么问题就简单多了。
用并查集记录两个点存不存在这样的${x}_{i}$和${y}_{i}$。
通过塔尖找到这个环,并记录这个环的信息,包括大小和哪个点属于这个环。
之后就是统计答案的过程了。
如果发现两个点不存在这样的${x}_{i}$和${y}_{i}$,则直接puts("-1 -1")即可。
如果发现它们在一个环上,那么就直接找到他们的LCA即可。
如果两个点都不在环上,那么我们就分别求出两个点到环的距离和环上要走的路径,然后比较大小输出答案即可。
如果其中一个点在环上,另一个点不在,则解法同上,毕竟那个在环上的点到环的距离为0。
建议你不要尝试这种解法,毕竟一开始我以为这就是正解,然后还忽悠旁边的547大佬调了一下午……
还有就是,这种算法显然常数比较大,时间复杂度不优,建议使用快读,不然会T到飞起。
代码时刻
#include<bits/stdc++.h>
using namespace std;
struct rec
{
int nxt;
int to;
}e[500001];//存图
int n,k;
int head[500001],cnt;
int dfn[500001],low[500001],sta[500001],ins[500001],c[500001],size[500001],lim[500001],num,tot,top;//塔尖用品
int fa[500001][30],depth[500001],tr[500001],sum;//LCA用品
bool vis[500001];
int f[500001];//并查集
inline int read(){//记得快读,不然会T
int ss=0;char bb=getchar();
while(bb<'0' || bb>'9')bb=getchar();
while(bb>='0' && bb<='9')ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
return ss;
}
inline void pre_work(){for(register int i=1;i<=n;i++)f[i]=i;}//并查集初始化
inline int find(register int x){return f[x]==x?x:f[x]=find(f[x]);}//并查集的find
inline void build(register int x,register int y){if(find(x)!=find(y))f[y]=x;}//并查集将两个点并在一起
inline void add(register int x,register int y)//建边
{
e[++cnt].nxt=head[x];
e[cnt].to=y;
head[x]=cnt;
}
inline void tarjan(register int x)//塔尖缩点
{
dfn[x]=low[x]=++num;
sta[++top]=x;
ins[x]=1;
for(register int i=head[x];i;i=e[i].nxt)
if(!dfn[e[i].to])
{
tarjan(e[i].to);
low[x]=min(low[x],low[e[i].to]);
}
else if(ins[e[i].to])
low[x]=min(low[x],dfn[e[i].to]);
if(dfn[x]==low[x])
{
tot++;
int y;
int flag=find(sta[top]);
do
{
y=sta[top--];
ins[y]=0;
c[y]=tot;
size[tot]++;//记录大小
if(size[tot]>1)lim[flag]=tot;//记录哪个点在这个环上
}while(x!=y);
}
}
inline void dfs(register int x,register int id)//dfs预处理属于哪个部分、深度和父亲
{
vis[x]=1;
tr[x]=id;
for(register int i=head[x];i;i=e[i].nxt)
if(!vis[e[i].to])
{
fa[e[i].to][0]=x;
depth[e[i].to]=depth[x]+1;
for(register int j=1;j<=18;j++)
fa[e[i].to][j]=fa[fa[e[i].to][j-1]][j-1];
if(c[x]==c[e[i].to])dfs(e[i].to,e[i].to);
else dfs(e[i].to,id);
}
}
inline pair<int,int> LCA(register int x,register int y)//求两个点到LCA的距离
{
register int ans1=0,ans2=0;
if(depth[x]<depth[y])swap(x,y);
for(register int i=18;i>=0;i--)
if(depth[fa[x][i]]>=depth[y])
{
x=fa[x][i];
ans1+=(1<<i);
}
if(x==y)return make_pair(ans1,0);
for(register int i=18;i>=0;i--)
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
ans1+=(1<<i);
ans2+=(1<<i);
}
return make_pair(ans1+1,ans2+1);
}
inline int LCA1(register int x,register int y)//求一个点到LCA的距离,用来卡常
{
register int ans=0;
if(depth[x]<depth[y])swap(x,y);
for(register int i=18;i>=0;i--)
if(depth[fa[x][i]]>=depth[y])
{
x=fa[x][i];
ans+=(1<<i);
}
if(x==y)return ans;
for(register int i=18;i>=0;i--)
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
ans+=(1<<i);
}
return ans;
}
inline void ptinf(register int x,register int y)//输出
{
if(find(x)!=find(y))//不存在这样的x[i],y[i]
{
puts("-1 -1");
return;
}
if(tr[x]==tr[y])//在一个环上
{
pair<int,int> flag=LCA(x,y);
if(depth[x]>depth[y])printf("%d %d\n",flag.first,flag.second);
else printf("%d %d\n",flag.second,flag.first);
return;
}
register int flag=find(x),flag1=LCA1(x,tr[x]),flag2=LCA1(y,tr[y]),flag3,flag4;
x=tr[x];
y=tr[y];
if(depth[x]<depth[y])
{
flag4=depth[y]-depth[x];
flag3=size[lim[flag]]-flag4;
}
else
{
flag3=depth[x]-depth[y];
flag4=size[lim[flag]]-flag3;
}//预处理到环的距离和环上要走的路径
if(max(flag1+flag3,flag2)<max(flag1,flag2+flag4))printf("%d %d\n",flag1+flag3,flag2);//比较大小输出答案
else if(max(flag1+flag3,flag2)>max(flag1,flag2+flag4))printf("%d %d\n",flag1,flag2+flag4);
else if(min(flag1+flag3,flag2)<min(flag1,flag2+flag4))printf("%d %d\n",flag1+flag3,flag2);
else if(min(flag1+flag3,flag2)>min(flag1,flag2+flag4))printf("%d %d\n",flag1,flag2+flag4);
else if(flag1+flag3<=flag2)printf("%d %d\n",flag1,flag2+flag4);
else printf("%d %d\n",flag1+flag3,flag2);
}
int main()
{
n=read(),k=read();
pre_work();
for(register int i=1;i<=n;i++)
{
int x=read();
build(x,i);
if(x!=i)add(x,i);
}
for(register int i=1;i<=n;i++)
if(!dfn[i])
{
depth[i]=1;
tarjan(i);
}
for(register int i=1;i<=tot;i++)
if(!vis[find(i)])
{
depth[find(i)]=1;
dfs(find(i),find(i));
}
for(register int i=1;i<=k;i++)
{
int x=read(),y=read();
ptinf(x,y);
}
return 0;
}
rp++
[BZOJ2791]:[Poi2012]Rendezvous(塔尖+倍增LCA)的更多相关文章
- [BZOJ2791][Poi2012]Rendezvous
2791: [Poi2012]Rendezvous Time Limit: 25 Sec Memory Limit: 128 MBSubmit: 95 Solved: 71[Submit][Sta ...
- 【BZOJ2791】[Poi2012]Rendezvous 倍增
[BZOJ2791][Poi2012]Rendezvous Description 给定一个n个顶点的有向图,每个顶点有且仅有一条出边.对于顶点i,记它的出边为(i, a[i]).再给出q组询问,每组 ...
- 【BZOJ 2791】 2791: [Poi2012]Rendezvous (环套树、树链剖分LCA)
2791: [Poi2012]Rendezvous Description 给定一个n个顶点的有向图,每个顶点有且仅有一条出边.对于顶点i,记它的出边为(i, a[i]).再给出q组询问,每组询问由两 ...
- [板子]倍增LCA
倍增LCA板子,没有压行,可读性应该还可以.转载请随意. #include <cstdio> #include <cstring> #include <algorithm ...
- 洛谷P3128 [USACO15DEC]最大流Max Flow [倍增LCA]
题目描述 Farmer John has installed a new system of pipes to transport milk between the stalls in his b ...
- Gym100685G Gadget Hackwrench(倍增LCA)
题目大概说一棵边有方向的树,q个询问,每次询问结点u是否能走到v. 倍增LCA搞即可: 除了par[k][u]表示u结点往上走2k步到达的结点, 再加上upp[k][u]表示u结点往上走2k步经过边的 ...
- Codeforces 418d Big Problems for Organizers [树形dp][倍增lca]
题意: 给你一棵有n个节点的树,树的边权都是1. 有m次询问,每次询问输出树上所有节点离其较近结点距离的最大值. 思路: 1.首先是按照常规树形dp的思路维护一个子树节点中距离该点的最大值son_di ...
- hdu 4674 Trip Advisor(缩点+倍增lca)
花了一天半的时间,才把这道题ac= = 确实是道好题,好久没敲这么长的code了,尤其是最后的判定,各种销魂啊~ 题目中给出的条件最值得关注的就是:每个点最多只能在一个环内->原图是由一个个边连 ...
- Tsinsen A1505. 树(张闻涛) 倍增LCA,可持久化线段树,DFS序
题目:http://www.tsinsen.com/A1505 A1505. 树(张闻涛) 时间限制:1.0s 内存限制:512.0MB 总提交次数:196 AC次数:65 平均分: ...
随机推荐
- c++ try_throw_catch异常处理
参考https://www.cnblogs.com/xiaojianliu/articles/8900795.html 在程序设计时,针对不同的异常情况,预先设定异常信息,在程序运行时,根据异常提示信 ...
- 分位数回归及其Python源码
分位数回归及其Python源码 天朗气清,惠风和畅.赋闲在家,正宜读书.前人文章,不得其解.代码开源,无人注释.你们不来,我行我上.废话少说,直入主题.o( ̄︶ ̄)o 我们要探测自变量 与因变量 的关 ...
- Maven项目构建利器05——Maven的生命周期
Maven各个构建环节执行的顺序: 不能打乱顺序, 必须按照既定的正确顺序(编译,测试.打包.部署)来执行Maven的核心程序中定义了抽象的生命周期, 生命周期中各个阶段的具体任务是由插件来完成的,可 ...
- aria2的安装与配置
aria2安装 安装 epel 源: yum install epel-release 然后直接安装: yum install aria2 -y 配置 Aria2 创建目录与配置文件 这一步需要切换到 ...
- Linux 开机文件系统发生错误
1 如果/目录没有损毁,那么开机的时候,屏幕会提示:press root password or ctrl +D,这个时候请输入root密码登陆系统,然后进行如下操作: 1)在光标处输入root密码登 ...
- (转) Delete/Truncate删除,释放表空间、降低高水位线、resize释放磁盘空间相关优化
硬盘空间不足,打算删除数据库中的多余数据,但删除数据后,硬盘硬盘空间不能释放.[delete后用:alter table table_name move truncate后用:alter tab ...
- Directed Roads CodeForces - 711D (基环外向树 )
ZS the Coder and Chris the Baboon has explored Udayland for quite some time. They realize that it co ...
- jquery 判断是否为空
jquery 判断是否为空 if(my_val == null || my_val == undefined || my_val==""){ console.log("我 ...
- gd_t结构 bd_t结构
gd_t在u-boot-2018.07-fmxx/include/asm-generic/global_data.h中定义 typedef struct global_data { bd_t * ...
- 深入理解JAVA虚拟机 垃圾收集器和内存分配策略
引用计数算法 很多教科书判断对象是否存活的算法是这样的:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器都为0的对象就是不可能再被使用的 ...