HDU 4297-One and One Story解题报告

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4297

这道题所给的图是一个森林,每一个子图都必有一个环(因为总边数为n),可以把环当做这棵树的根节点,那么如果两个点不在一棵树上,一定互相不可达,如果在一棵树上,如果两个点在同一棵子树上,那么一起到的点就是LCA,所以要维护森林的LCA,如果不在同一棵子树上,则要到各自的根节点,然后沿着环方向分别判断哪种情况是最优解。

View Code
  1 #pragma comment(linker, "/STACK:10240000000,10240000000")
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<cmath>
  6 #define N 500005
  7 #define max(a,b) ((a)>(b)?(a):(b))
  8 #define min(a,b) ((a)<(b)?(a):(b))
  9 using namespace std;
 10 int nxt[N];
 11 int root[N];//点是否在环上,在环上是环上的第几个点
 12 int father[N];//在环上的父节点
 13 int num[N];//所在环的节点个数
 14 int f[N];//并查集判断是否是在一个树上,并且可以确定环
 15 int list[2*N];
 16 int fir[2*N],ti;//rmq解决LCA问题
 17 bool used[N];
 18 int deep[2*N];
 19 int n;
 20 int head[N],cnt;
 21 struct node
 22 {
 23     int v,next;
 24 };
 25 node edge[N];//要加反向边
 26 void add(int u,int v)
 27 {
 28     edge[cnt].v=v,edge[cnt].next=head[u],head[u]=cnt++;
 29 }
 30 int find(int x)
 31 {
 32     int r=x;
 33     while(r!=f[r])
 34     r=f[r];
 35     int t1;
 36     while(x!=f[x])
 37     {
 38         t1=f[x];
 39         f[x]=r;
 40         x=t1;
 41     }
 42     return r;
 43 }
 44 void uni(int x,int y)
 45 {
 46     int t1=find(x);
 47     int t2=find(y);
 48     if(t1==t2)//发现一个点和其所要连接的点在一个集合时,说明产生了环
 49     {
 50         int cnt1=0;
 51         int temp=x;
 52         do
 53         {
 54             root[x]=++cnt1;
 55             x=nxt[x];
 56         }while(!root[x]);
 57         x=temp;
 58         num[x]=cnt1;
 59         x=nxt[x];
 60         while(x!=temp)
 61         {
 62             num[x]=cnt1;
 63             x=nxt[x];
 64         }
 65     }
 66     else
 67     f[t1]=t2;
 68 }
 69 int dp[2*N][22];
 70 void makermq()
 71 {
 72     int i,j;
 73     for(i=1;i<ti;i++)
 74     dp[i][0]=deep[i];
 75     for(j=1;(1<<j)<ti;j++)
 76     {
 77         for(i=1;i+(1<<j)-1<ti;i++)
 78         dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
 79     }
 80 }
 81 int rmq(int s,int e)
 82 {
 83     int k=(int)(log((e-s+1)*1.0)/log(2.0));
 84     return min(dp[s][k],dp[e-(1<<k)+1][k]);
 85 }
 86 void init()
 87 {
 88     memset(dp,0x3f,sizeof(dp));
 89     memset(root,0,sizeof(root));
 90     memset(num,0,sizeof(num));
 91     memset(head,-1,sizeof(head));
 92     memset(used,0,sizeof(used));
 93     memset(deep,0,sizeof(deep));
 94     memset(fir,0,sizeof(fir));
 95     memset(father,0,sizeof(father));
 96     memset(list,0,sizeof(list));
 97     cnt=0;
 98     ti=1;
 99     int i;
100     for(i=1;i<=n;i++)
101     {
102         f[i]=i;
103     }
104 }
105 void dfs(int u,int d,int v)
106 {
107     used[u]=true;
108     if(root[u])
109     {
110         d=1;
111         v=u;
112     }
113     list[ti]=u;
114     deep[ti]=d;
115     fir[u]=ti;
116     ti++;
117     father[u]=v;
118     int i;
119     for(i=head[u];i>=0;i=edge[i].next)
120     {
121         if(!used[edge[i].v])
122         {
123             dfs(edge[i].v,d+1,v);
124             list[ti]=u;
125             deep[ti]=d;
126             ti++;
127         }
128     }
129 }
130 int main()
131 {
132     int i,j,m,a,b,temp,t1,t2;
133     while(scanf("%d%d",&n,&m)!=EOF)
134     {
135         init();
136         for(i=1;i<=n;i++)
137         {
138             scanf("%d",&nxt[i]);
139             add(nxt[i],i);
140             uni(i,nxt[i]);
141         }
142         for(i=1;i<=n;i++)
143         if(root[i]&&(!used[i]))
144         dfs(i,1,i);
145         makermq();
146         for(i=1;i<=m;i++)
147         {
148             scanf("%d%d",&a,&b);
149             if(find(a)!=find(b))
150             {
151                 printf("-1 -1\n");
152                 continue;
153             }
154             if(father[a]==father[b])
155             {
156                 t1=a,t2=b;
157                 if(fir[a]>fir[b])//这里要特别注意
158                 {
159                     t1=b,t2=a;
160                 }
161                 temp=rmq(fir[t1],fir[t2]);
162                 printf("%d %d\n",deep[fir[a]]-temp,deep[fir[b]]-temp);
163             }
164             else
165             {
166                 temp=num[father[a]];
167                 t1=deep[fir[a]]-1;
168                 t2=deep[fir[b]]-1;
169                 int fa=father[a];
170                 int fb=father[b];
171                 int ans1=t1,ans2=t2;
172                 if(root[fa]<root[fb])
173                 {
174                     ans1+=(root[fb]-root[fa]);
175                     ans2+=(root[fa]+num[fa]-root[fb]);
176                 }
177                 else
178                 {
179                     ans1+=(root[fb]+num[fb]-root[fa]);
180                     ans2+=(root[fa]-root[fb]);
181                 }
182                 if(max(ans1,t2)<max(t1,ans2))
183                 {
184                     printf("%d %d\n",ans1,t2);
185                 }
186                 else if(max(ans1,t2)>max(t1,ans2))
187                 {
188                     printf("%d %d\n",t1,ans2);
189                 }
190                 else
191                 {
192                     if(min(ans1,t2)<min(t1,ans2))
193                     {
194                         printf("%d %d\n",ans1,t2);
195                     }
196                     else if(min(ans1,t2)>min(t1,ans2))
197                     {
198                         printf("%d %d\n",t1,ans2);
199                     }
200                     else
201                     {
202                         if(ans1>=t2)
203                         {
204                             printf("%d %d\n",ans1,t2);
205                         }
206                         else
207                         {
208                             printf("%d %d\n",t1,ans2);
209                         }
210                     }
211                 }
212             }
213         }
214     }
215     return 0;
216 }

 

转载于:https://www.cnblogs.com/caozhenhai/archive/2012/09/21/2697350.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值