花了一天半的时间,才把这道题ac= =

确实是道好题,好久没敲这么长的code了,尤其是最后的判定,各种销魂啊~

题目中给出的条件最值得关注的就是:每个点最多只能在一个环内->原图是由一个个边连通分量以树形连接组成的->做无向图缩点后,得到的是一个树形结构。

题目要求:u->v,必须经过p,且不能重复经过同一个点,即在树上从u到v做一笔画。

开始先想到汉密尔顿迹,不过那是走全部点的。利用已获得的树形结构,通过lca来判断p,这就是一个合理的作法。

注意:由于是任意建树,p不一定是u,v的lca,纠结了好久才想出了一个方法:x=lca(u,v),然后遍历v->x,u->x两条路径,找p——会超时的,太繁琐了。朋友提出了,找u,v,p中两两的lca:if(lca(u,v)==lca(u,p)&&lca(v,p)==p)就可以判断缩点后的p在u,v路径上。为什么是缩点后的呢?自己想吧,后面判断要用到的。

缩点和倍增lca自己学吧,我也是为了做这道题才学了lca T^T 之前连“爬楼梯”都不会。

这道题的精彩之处就在于最后的判定。

从u==v&&v==p 三点共点->两点共点,再到缩点后三点共点,两点共点要一一讨论。

其中容易忽视的:1、u,p缩点后cu,cp共点,点u在向cv出发的方向的出口上。(若要经过p,u点必然要重复经过)

2、环cp在u->v的路径上,但进出在同一点,并且不是点p。(若要经过p,该出入点必然要重复经过)

其他一些容易犯的错误我都在code中注明。不过这道题的价值也就体现于此,自己做吧。再次膜拜现场出题的大牛。

注:TLE了好久,后来就wa,为了调程序,编了一组七个点的数据,从111->777共344组测试,code中附上了数据以及自己代码中找到的三组错误测试,修正后就ac了。

 #pragma comment(linker,"/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<stack>
#include<queue>
#include<algorithm>
using namespace std; const int MAXN=;
const int MAXM=;
const int POW=; struct Edge{
int v,next;
int vis,bridge;
Edge(){}
Edge(int _v,int _next):v(_v),next(_next),vis(){}
}edge[MAXM<<]; int head[MAXN],tol;
int stk[MAXN],top;
int pre[MAXN],low[MAXN],bccno[MAXN],bcc_cnt,dfs_clock;
vector<int>G[MAXN];
int d[MAXN],p[MAXN][POW]; void init()
{
tol=;
memset(head,-,sizeof(head));
} void add(int u,int v)
{
edge[tol]=Edge(v,head[u]);
head[u]=tol++;
} void build(int m)
{
int u,v;
init();
for(int i=;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
} void dfs(int u)
{
int v;
pre[u]=low[u]=++dfs_clock;
stk[top++]=u;
for(int i=head[u];i!=-;i=edge[i].next)
{
if(edge[i].vis)
continue ;
edge[i].vis=edge[i^].vis=;
v=edge[i].v; if(!pre[v]){
dfs(v);
low[u]=min(low[u],low[v]);
}else if(!bccno[v])
low[u]=min(low[u],pre[v]);
} if(low[u]==pre[u]){
bcc_cnt++;
do{
v=stk[--top];
bccno[v]=bcc_cnt;
}while(v!=u);
}
} void tarjin(int n)
{
bcc_cnt=dfs_clock=;
memset(pre,,sizeof(pre));
memset(bccno,,sizeof(bccno)); top=;
for(int i=;i<=n;i++)
if(!pre[i])
dfs(i);
} void rebuild(int n)
{
for(int i=;i<=bcc_cnt;i++) //!!把bcc_cnt写成n = =
G[i].clear();
for(int i=;i<=n;i++)
{ for(int j=head[i];j!=-;j=edge[j].next)
{
int v=edge[j].v;
if(bccno[i]!=bccno[v])
G[bccno[i]].push_back(v); //!!桥,这里写v而不是bccno[v],是为了后面judge()判断环的出入口
}
}
} void DFS(int u,int fa)
{
d[u]=d[fa]+;
p[u][]=fa;
for(int i=;i<POW;i++)
p[u][i]=p[p[u][i-]][i-];
int sz=G[u].size();
for(int i=;i<sz;i++)
{
int v=bccno[G[u][i]];
if(v==fa)
continue;
DFS(v,u);
}
} int lca( int a, int b ){
if( d[a] > d[b] ) a ^= b, b ^= a, a ^= b;
if( d[a] < d[b] ){
int del = d[b] - d[a];
for( int i = ; i < POW; i++ ) if(del&(<<i)) b=p[b][i];
}
if( a != b ){
for( int i = POW-; i >= ; i-- )
if( p[a][i] != p[b][i] )
a = p[a][i] , b = p[b][i];
a = p[a][], b = p[b][];
}
return a;
} void LCA(int n)//这里只是处理了一下新图中各个点的深度
{
d[]=;
DFS(,);
} int Double(int a,int b)//倍增
{
int del=d[a]-d[b]-;
for(int i=;i<POW;i++)
if(del&(<<i))
a=p[a][i];
return a;
} int judge(int u,int v)//返回值是该方向上环的出入口
{
int sz=G[u].size();
for(int i=;i<sz;i++)
if(bccno[G[u][i]]==bccno[v])
return G[u][i];
} void solve()
{
int k;
int u,v,w;
scanf("%d",&k);
for(int i=;i<=k;i++)
{
scanf("%d%d%d",&u,&v,&w);
int a=bccno[u],b=bccno[v],c=bccno[w];
if(u==v&&v==w)
printf("Yes\n");
else if(u==w||v==w)//!!注意:即使考虑了两点uw(或vw)缩点后重合,目标点u(或v)不能作为出口,但u,w共点是可行的
printf("Yes\n");
else if(u==v)
printf("No\n");
else if(bccno[u]==bccno[v]&&bccno[u]==bccno[w])
printf("Yes\n");
else if(bccno[u]==bccno[v])
printf("No\n");
else if(bccno[u]==bccno[w]){
int flog;
int pp=lca(a,b);//!!注意:当两点缩点后重合,无法直接判定第三点在上方还是下方,需要利用lca
if(pp==a){
b=Double(b,c);//由b向c做倍增
flog=judge(b,u);
}else
flog=judge(p[a][],u);
if(flog!=u)
printf("Yes\n");
else
printf("No\n");
}else if(bccno[v]==bccno[w]){
int flog;
int pp=lca(a,b);//!!同上
if(pp==b){
a=Double(a,c);//由a向c做倍增
flog=judge(a,v);
}else
flog=judge(p[b][],v);
if(flog!=v)
printf("Yes\n");
else
printf("No\n");
}else {
int p1,p2,p3; p1=lca(a,b);
p2=lca(a,c);
p3=lca(b,c);
if(p1==p2&&p3==c){//判定点p是否能经过:求出两两lca
int flog1,flog2;
b=Double(b,c);
flog1=judge(b,w); if(p1==c){//!!注意:当点p即为lca(u,v)时,p的另一个入口就不能从其父亲查找了,要在另一支路上做倍增
a=Double(a,c);
flog2=judge(a,w);
}else
flog2=judge(p[c][],w); if(flog1!=w&&flog2!=w&&flog1==flog2)
printf("No\n");
else
printf("Yes\n");
}
else if(p1==p3&&p2==c){//这里上下两步其实是重复处理:p3==c表示p点在v支路上;反之在u支路上
int flog1,flog2;
a=Double(a,c);
flog1=judge(a,w); if(p1==c){
b=Double(b,c);
flog2=judge(b,w);
}else
flog2=judge(p[c][],w); if(flog1!=w&&flog2!=w&&flog1==flog2)
printf("No\n");
else
printf("Yes\n");
}else
printf("No\n");
}
}
} int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
build(m);
tarjin(n);
rebuild(n);
LCA(n);
solve();
}
return ;
}
/*
7 7
1 2
2 3
2 4
3 4
3 5
4 6
4 7
3
1 2 3
1 6 2
1 2 1
*/

hdu 4674 Trip Advisor(缩点+倍增lca)的更多相关文章

  1. hdu 2586 How far away ?倍增LCA

    hdu 2586 How far away ?倍增LCA 题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2586 思路: 针对询问次数多的时候,采取倍增 ...

  2. H - Rescue the Princess ZOJ - 4097 (tarjan缩点+倍增lca)

    题目链接: H - Rescue the Princess  ZOJ - 4097 学习链接: zoj4097 Rescue the Princess无向图缩点有重边+lca - lhc..._博客园 ...

  3. HDU 2887 Watering Hole(MST + 倍增LCA)

    传送门 总算是做上一道LCA的应用题了... 题意:有$n$个牧场, $m$根管道分别连接编号为$u,v$的牧场花费$p_{i}$,在第$i$个牧场挖口井需要花费$w_{i}$,有$P$根管道直接连通 ...

  4. HDU4674 Trip Advisor

    Problem Description There is a strange country somewhere which its transportation network was built ...

  5. HDU2586 How far away? —— 倍增LCA

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 How far away ? Time Limit: 2000/1000 MS (Java/Ot ...

  6. 洛谷P3379lca,HDU2586,洛谷P1967货车运输,倍增lca,树上倍增

    倍增lca板子洛谷P3379 #include<cstdio> struct E { int to,next; }e[]; ],anc[][],log2n,deep[],n,m,s,ne; ...

  7. [BZOJ2791]:[Poi2012]Rendezvous(塔尖+倍增LCA)

    题目传送门 题目描述 给定一个有n个顶点的有向图,每个顶点有且仅有一条出边.每次询问给出两个顶点${a}_{i}$和${b}_{i}$​​,求满足以下条件的${x}_{i}$和${y}_{i}$:   ...

  8. [板子]倍增LCA

    倍增LCA板子,没有压行,可读性应该还可以.转载请随意. #include <cstdio> #include <cstring> #include <algorithm ...

  9. 洛谷P3128 [USACO15DEC]最大流Max Flow [倍增LCA]

    题目描述 Farmer John has installed a new system of  pipes to transport milk between the  stalls in his b ...

随机推荐

  1. vi 常用技巧

    vi/vim : Visual Interface/VI iMproved VI命令可以说是Unix/Linux世界里最常用的编辑文件的命令了,但是因为它的命令集众多,很多人都不习惯使用它,其实您只需 ...

  2. git撤销删除

    问题描述:     使用git时本地文件删除了,提交至github,希望撤销修改,找回源文件 问题解决: (1)查看git log,查看日志信息 注:     使用 git log 可以查看提交的日志 ...

  3. PHPer 为什么会被 Javaer 鄙视?

    最近看了知乎上的一个话题 「在工作中,为什么 Java 程序员常常瞧不起 PHP 程序员?」 个人从业多年,用过的后端语言 ASP.ASP.NET.Java.PHP.Node.js.Python,如果 ...

  4. hdu 4417 Super Mario 离线线段树

    思路:将点按值从小到大排序,询问按h从小到大排序. 在建立线段树,按h的大小更新树并得到该次查询的结果! 代码如下: #include<iostream> #include<stdi ...

  5. hdu 1850 Being a Good Boy in Spring Festival 博弈论

    求可行的方案数!! 代码如下: #include<stdio.h> ]; int main(){ int n,m; while(scanf("%d",&n)&a ...

  6. linux入门教程(十) 文档的压缩与打包

    在windows下我们接触最多的压缩文件就是.rar格式的了.但在linux下这样的格式是不能识别的,它有自己所特有的压缩工具.但有一种文件在windows和linux下都能使用那就是.zip格式的文 ...

  7. 李洪强iOS开发之零基础学习iOS开发【02-C语言】03-关键字、标识符、注释

    上一讲中已经创建了第一个C语言程序,知道了C程序是由函数构成的,这讲继续学习C语言的一些基本语法.C语言属于一门高级语言,其实,所有的高级语言的基本语法组成部分都是一样的,只是表现形式不太一样.就好像 ...

  8. lintcode 中等题: reverse linked list II 翻转链表II

    题目 翻转链表 II 翻转链表中第m个节点到第n个节点的部分 样例 给出链表1->2->3->4->5->null, m = 2 和n = 4,返回1->4-> ...

  9. Pythonxy下载链接

    http://pythonxy.connectmv.com/ 版权声明:本文为博主原创文章,未经博主允许不得转载.

  10. [hackerrank]Even Odd Query

    https://www.hackerrank.com/contests/w5/challenges 简单题,注意整数的0次方是1,奇数. #include <iostream> #incl ...