• 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
  • 相关阅读:
    慕课前端入门-HTML5属性变化
    黑马jQuery教程4
    黑马jQuery教程3
    黑马JQuery教程2
    2017-03-15
    按钮图标化
    AES MFC实现
    CButtonST类简介使用方法
    VS资源编辑器常见错误RC1000到RC1208
    MFC单文档程序添加HTML帮助支持
  • 原文地址:https://www.cnblogs.com/zstu-abc/p/3262941.html
Copyright © 2020-2023  润新知