CodeforcesD. Aztec Catacombs

$n \leq 300000$的完全无向图,每条边有可行和不可行的状态,一开始只有$m \leq 300000$条边是可行的,给出。每次从$x$走到$y$时,所有与$x$相连的边的可行/不可行状态会改变。问从1最少走几步到$n$。

先考虑只走原来有的路,如果走原来有的路能到$n$,那么这可能是一种可行方案。如果要利用边状态的改变来到达$n$,最优地可以找一个距离为2的点,走到他,再回到1,然后一步到$n$,读者自证不难(考虑每一步如果会出现更优的利用原图没有的边来走会发生什么)。也就是说,直接走路程$\leq4$时直接走,否则找个距离2的绕一圈。

如果没有距离2的点,又不能到$t$,那么1所在的连通分量大概是个菊花,但菊花的叶子是有连边的。这时从1走出去再也回不到1,可删之,与1相连的点变成若干连通块。新到那个点叫$x$,如果$x$的连通块里有个距离$x$为2的点,那可以用上面的方法得到5的答案;如果没有,说明这个点与分量里的所有点有连边,那还要递归下去找吗?不必了,如果枚举所有与1相连的点做如此判断,得不到5的答案就无解了。证明可以联系大前提:没有与1距离大于1的点。

 1 //#include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 //#include<math.h>
 5 //#include<set>
 6 //#include<queue>
 7 //#include<vector>
 8 #include<algorithm>
 9 #include<stdlib.h>
10 using namespace std;
11 
12 #define LL long long
13 int qread()
14 {
15     char c; int s=0,f=1; while ((c=getchar())<'0' || c>'9') (c=='-') && (f=-1);
16     do s=s*10+c-'0'; while ((c=getchar())>='0' && c<='9'); return s*f;
17 }
18 
19 //Pay attention to '-' , LL and double of qread!!!!
20 
21 int n,m;
22 #define maxn 300011
23 struct Edge{int to,next;}edge[maxn<<1]; int first[maxn],le=2;
24 void in(int x,int y) {Edge &e=edge[le]; e.to=y; e.next=first[x]; first[x]=le++;}
25 void insert(int x,int y) {in(x,y); in(y,x);}
26 
27 int dis[maxn],pre[maxn],ufs[maxn],que[maxn],head,tail,du[maxn],size[maxn];
28 int find(int x) {return x==ufs[x]?x:(ufs[x]=find(ufs[x]));}
29 void Union(int x,int y) {if ((x=find(x))!=(y=find(y))) ufs[x]=y,size[y]+=size[x];}
30 void bfs(int s)
31 {
32     for (int i=1;i<=n;i++) dis[i]=0x3f3f3f3f,pre[i]=0;
33     head=tail=1; que[tail++]=s; dis[s]=0;
34     while (head!=tail)
35     {
36         int x=que[head++];
37         for (int i=first[x];i;i=edge[i].next)
38         {
39             Edge &e=edge[i]; if (e.to==1) continue;
40             if (dis[e.to]==0x3f3f3f3f)
41             {
42                 dis[e.to]=dis[x]+1;
43                 pre[e.to]=x;
44                 que[tail++]=e.to;
45             }
46         }
47     }
48 }
49 
50 int main()
51 {
52     n=qread(); m=qread();
53     for (int i=1,x,y;i<=m;i++) {x=qread(); y=qread(); insert(x,y);}
54     bfs(1);
55     if (dis[n]<=4)
56     {
57         printf("%d\n1 ",dis[n]);
58         int ans[10],lans=0;
59         for (int i=n;i!=1;i=pre[i]) ans[++lans]=i;
60         for (int i=lans;i;i--) printf("%d ",ans[i]);
61         return 0;
62     }
63     for (int i=1;i<=n;i++) if (dis[i]==2)
64     {
65         printf("4\n1 %d %d 1 %d",pre[i],i,n);
66         return 0;
67     }
68     for (int i=2;i<n;i++) du[i]=-1,ufs[i]=i,size[i]=1;
69     for (int i=2;i<n;i++) if (dis[i]==1)
70         for (int j=first[i];j;j=edge[j].next)
71         {
72             if (edge[j].to!=1) Union(i,edge[j].to);
73             du[i]++;
74         }
75     for (int i=2;i<n;i++) if (dis[i]==1)
76     {
77         int u=size[find(i)];
78         if (u-1==du[i]) continue;
79         bfs(i);
80         for (int j=2;j<n;j++) if (dis[j]==2)
81         {
82             printf("5\n1 %d %d %d %d %d",i,pre[j],j,i,n);
83             return 0;
84         }
85     }
86     puts("-1");
87     return 0;
88 }
View Code

 

转载于:https://www.cnblogs.com/Blue233333/p/9191117.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值