花了一天半的时间,才把这道题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. java 获取获取字符串编码格式

    public static String getEncoding(String str) { String encode = "GB2312"; try { if (str.equ ...

  2. c++ void,内存操作函数

    void的含义 void的字面意思是“无类型”, void * 则为“无类型指针”, void * 可以指向任何类型的数据 void几乎只有“注释”和限制程序的作用,因为从来没有人会定义一个void变 ...

  3. Ext学习-布局介绍

    1.目标    了解ExtJS中的关于布局和组建的相关原理,并学习相关的布局方式 2.内容   1.布局和组件的相关原理   2.常见的布局方式 3.学习流程    1.首先应该学习一下布局和组件的相 ...

  4. 移动端页面使用rem来做适配

    文/九彩拼盘(简书作者)原文链接:http://www.jianshu.com/p/eb05c775d3c6著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. rem介绍 rem(font ...

  5. [转载]C#如何在webBrowser1控件通过TagName,Name查找元素(没有ID时)遍历窗体元素

    //防止页面多次刷新页面执行 ) { string GetUserName = System.Configuration.ConfigurationSettings.AppSettings[" ...

  6. ios开发之触摸&手势识别

    概要: 4个触摸事件.6个手势识别.响应者链条 1.4个触摸事件 1> 触摸事件主要是针对视图的,包括 - (void)touchesBegan:(NSSet *)touches withEve ...

  7. zju 1037 Gridland(找规律,水题)

    题目链接 多写几个案例,根据数据的奇偶性,就能找到规律了 #include<stdio.h> int main() { int t,n,m; double ans; scanf(" ...

  8. Selenium WebDriver 中鼠标和键盘事件分析及扩展(转)

    本文将总结 Selenium WebDriver 中的一些鼠标和键盘事件的使用,以及组合键的使用,并且将介绍 WebDriver 中没有实现的键盘事件(Keys 枚举中没有列举的按键)的扩展.举例说明 ...

  9. 欧拉工程第57题:Square root convergents

    题目链接 Java程序 package projecteuler51to60; import java.math.BigInteger; import java.util.Iterator; impo ...

  10. *[codility]MinAvgTwoSlice

    https://codility.com/demo/take-sample-test/min_avg_two_slice 此题要求一个数组子段的最小的平均数(返回第一个数字的index).刚开始想记录 ...