hdu 4674 Trip Advisor(缩点+倍增lca)

花了一天半的时间,才把这道题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了。

  1 #pragma comment(linker,"/STACK:1024000000,1024000000")
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<stack>
  5 #include<queue>
  6 #include<algorithm>
  7 using namespace std;
  8 
  9 const int MAXN=111111;
 10 const int MAXM=155555;
 11 const int POW=18;
 12 
 13 struct Edge{
 14     int v,next;
 15     int vis,bridge;
 16     Edge(){}
 17     Edge(int _v,int _next):v(_v),next(_next),vis(0){}
 18 }edge[MAXM<<1];
 19 
 20 int head[MAXN],tol;
 21 int stk[MAXN],top;
 22 int pre[MAXN],low[MAXN],bccno[MAXN],bcc_cnt,dfs_clock;
 23 vector<int>G[MAXN];
 24 int d[MAXN],p[MAXN][POW];
 25 
 26 void init()
 27 {
 28     tol=0;
 29     memset(head,-1,sizeof(head));
 30 }
 31 
 32 void add(int u,int v)
 33 {
 34     edge[tol]=Edge(v,head[u]);
 35     head[u]=tol++;
 36 }
 37 
 38 void build(int m)
 39 {
 40     int u,v;
 41     init();
 42     for(int i=1;i<=m;i++)
 43     {
 44         scanf("%d%d",&u,&v);
 45         add(u,v);
 46         add(v,u);
 47     }
 48 }
 49 
 50 void dfs(int u)
 51 {
 52     int v;
 53     pre[u]=low[u]=++dfs_clock;
 54     stk[top++]=u;
 55     for(int i=head[u];i!=-1;i=edge[i].next)
 56     {
 57         if(edge[i].vis)
 58             continue ;
 59         edge[i].vis=edge[i^1].vis=1;
 60         v=edge[i].v;
 61 
 62         if(!pre[v]){
 63             dfs(v);
 64             low[u]=min(low[u],low[v]);
 65         }else if(!bccno[v])
 66             low[u]=min(low[u],pre[v]);
 67     }
 68 
 69     if(low[u]==pre[u]){
 70         bcc_cnt++;
 71         do{
 72             v=stk[--top];
 73             bccno[v]=bcc_cnt;
 74         }while(v!=u);
 75     }
 76 }
 77 
 78 void tarjin(int n)
 79 {
 80     bcc_cnt=dfs_clock=0;
 81     memset(pre,0,sizeof(pre));
 82     memset(bccno,0,sizeof(bccno));
 83 
 84     top=0;
 85     for(int i=1;i<=n;i++)
 86         if(!pre[i])
 87             dfs(i);
 88 }
 89 
 90 void rebuild(int n)
 91 {
 92     for(int i=1;i<=bcc_cnt;i++)     //!!把bcc_cnt写成n = =
 93         G[i].clear();
 94     for(int i=1;i<=n;i++)
 95     {
 96 
 97         for(int j=head[i];j!=-1;j=edge[j].next)
 98         {
 99             int v=edge[j].v;
100             if(bccno[i]!=bccno[v])
101                 G[bccno[i]].push_back(v);    //!!桥,这里写v而不是bccno[v],是为了后面judge()判断环的出入口
102         }
103     }
104 }
105 
106 void DFS(int u,int fa)
107 {
108     d[u]=d[fa]+1;
109     p[u][0]=fa;
110     for(int i=1;i<POW;i++)
111         p[u][i]=p[p[u][i-1]][i-1];
112     int sz=G[u].size();
113     for(int i=0;i<sz;i++)
114     {
115         int v=bccno[G[u][i]];
116         if(v==fa)
117             continue;
118         DFS(v,u);
119     }
120 }
121 
122 int lca( int a, int b ){
123     if( d[a] > d[b] ) a ^= b, b ^= a, a ^= b;
124     if( d[a] < d[b] ){
125         int del = d[b] - d[a];
126         for( int i = 0; i < POW; i++ ) if(del&(1<<i)) b=p[b][i];
127     }
128     if( a != b ){
129         for( int i = POW-1; i >= 0; i-- )
130             if( p[a][i] != p[b][i] )
131                  a = p[a][i] , b = p[b][i];
132         a = p[a][0], b = p[b][0];
133     }
134     return a;
135 }
136 
137 void LCA(int n)//这里只是处理了一下新图中各个点的深度
138 {
139     d[1]=0;
140     DFS(1,1);
141 }
142 
143 int Double(int a,int b)//倍增
144 {
145     int del=d[a]-d[b]-1;
146     for(int i=0;i<POW;i++)
147         if(del&(1<<i))
148             a=p[a][i];
149     return a;
150 }
151 
152 int judge(int u,int v)//返回值是该方向上环的出入口
153 {
154     int sz=G[u].size();
155     for(int i=0;i<sz;i++)
156         if(bccno[G[u][i]]==bccno[v])
157             return G[u][i];
158 }
159 
160 void solve()
161 {
162     int k;
163     int u,v,w;
164     scanf("%d",&k);
165     for(int i=1;i<=k;i++)
166     {
167         scanf("%d%d%d",&u,&v,&w);
168         int a=bccno[u],b=bccno[v],c=bccno[w];
169         if(u==v&&v==w)
170             printf("Yes
");
171         else if(u==w||v==w)//!!注意:即使考虑了两点uw(或vw)缩点后重合,目标点u(或v)不能作为出口,但u,w共点是可行的
172             printf("Yes
");
173         else if(u==v)            
174             printf("No
");
175         else if(bccno[u]==bccno[v]&&bccno[u]==bccno[w])
176             printf("Yes
");
177         else if(bccno[u]==bccno[v])
178             printf("No
");
179         else if(bccno[u]==bccno[w]){
180             int flog;
181             int pp=lca(a,b);//!!注意:当两点缩点后重合,无法直接判定第三点在上方还是下方,需要利用lca
182             if(pp==a){
183                 b=Double(b,c);//由b向c做倍增
184                 flog=judge(b,u);
185             }else
186                 flog=judge(p[a][0],u);
187             if(flog!=u)
188                 printf("Yes
");
189             else
190                 printf("No
");
191         }else if(bccno[v]==bccno[w]){
192             int flog;
193             int pp=lca(a,b);//!!同上
194             if(pp==b){
195                 a=Double(a,c);//由a向c做倍增
196                 flog=judge(a,v);
197             }else
198                 flog=judge(p[b][0],v);
199             if(flog!=v)
200                 printf("Yes
");
201             else
202                 printf("No
");
203         }else {
204             int p1,p2,p3;
205 
206             p1=lca(a,b);
207             p2=lca(a,c);
208             p3=lca(b,c);
209             if(p1==p2&&p3==c){//判定点p是否能经过:求出两两lca
210                 int flog1,flog2;
211                 b=Double(b,c);
212                 flog1=judge(b,w);
213 
214                 if(p1==c){//!!注意:当点p即为lca(u,v)时,p的另一个入口就不能从其父亲查找了,要在另一支路上做倍增
215                     a=Double(a,c);
216                     flog2=judge(a,w);
217                 }else
218                     flog2=judge(p[c][0],w);
219 
220                 if(flog1!=w&&flog2!=w&&flog1==flog2)
221                     printf("No
");
222                 else
223                     printf("Yes
");
224             }
225             else if(p1==p3&&p2==c){//这里上下两步其实是重复处理:p3==c表示p点在v支路上;反之在u支路上
226                 int flog1,flog2;
227                 a=Double(a,c);
228                 flog1=judge(a,w);
229 
230                 if(p1==c){
231                     b=Double(b,c);
232                     flog2=judge(b,w);
233                 }else
234                     flog2=judge(p[c][0],w);
235 
236                 if(flog1!=w&&flog2!=w&&flog1==flog2)
237                     printf("No
");
238                 else
239                     printf("Yes
");
240             }else
241                 printf("No
");
242         }
243     }
244 }
245 
246 int main()
247 {
248     int n,m;
249     while(~scanf("%d%d",&n,&m))
250     {
251         build(m);
252         tarjin(n);
253         rebuild(n);
254         LCA(n);
255         solve();
256     }
257     return 0;
258 }
259 /*
260 7 7
261 1 2
262 2 3
263 2 4
264 3 4
265 3 5
266 4 6
267 4 7
268 3
269 1 2 3
270 1 6 2
271 1 2 1
272 */
View Code
原文地址:https://www.cnblogs.com/zstu-abc/p/3262941.html