cdcq

梦幻小鱼干

导航

【SWERC 2019-20】K Birdwatching

题意:

给你一个无向图,和一个关键点T,问你有哪些点Ti不经过边Ti->T就到不了T?

边数点数<=1e5

 

练习的时候队友都说没看懂,打完听孟神复述了题意,我:???

这个题目很奇怪哦,看上去挺反直觉的,但是确实是河里的

那么遇到有向图,常见技巧,建反向边

这道题建立反向边的好处是把问题转化为从T出发,不经过T->Ti就到不了的Ti有多少个,把多起点转化为单起点,更易于思考

一个朴素的想法是分别以每个有从T->Ti边的Ti作为起点进行bfs,然后统计哪些点经过一次,但这样复杂度O(n^2),会被菊花图或水母图卡

接着发现性质:

对于满足题目要求的一个Ti,只能从唯一的一个和T相邻的点出发访问到(否则就不满足条件了)

那么每个点至多只有必要被两次bfs访问,第三次访问到的时候,由于bfs的特点,此点后面的所有点也都至少访问了两次,这些点都已经从答案中被排除掉了,因此不必再遍历一次

这样时间复杂度就优化到了O(n)

细节:

如果Ti不存在T->Ti的边,那么即使只用一种方法从T到Ti,也不能被算作答案,样例很良心,包含了这种情况

要判断这个情况也非常好处理,如果某个Ti只存在一种方法从T到Ti,那么只要存在边T-Ti,这一定就是唯一的方法

因此和T连边的点,和只存在一次bfs访问的点取交集就是答案

这题确实挺简单的,但是队友没看懂,就没想,果然看题还是十分重要的啊

 

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 struct edg{int nxt,y;}e[110000];  int lk[110000],ltp=0;
 5 void ist(int x,int y){
 6     e[++ltp]=(edg){lk[x],y};  lk[x]=ltp;
 7 }
 8 int n,m,o;
 9 int f[110000];
10 int g[110000];
11 int q[110000],hd=0;
12 void bfs(int x){
13     if(g[x]>1)  return ;
14     q[hd=1]=x;
15     f[x]=1;
16     g[x]++;
17     for(int k=1;k<=hd;++k){
18         for(int i=lk[q[k]];i;i=e[i].nxt){
19             if(g[e[i].y]==2)  continue;
20             if(f[e[i].y]==1)  continue;
21             f[e[i].y]=1;
22             g[e[i].y]++;
23             q[++hd]=e[i].y;
24         }
25     }
26     for(int k=1;k<=hd;++k)
27         f[q[k]]=0;
28 }
29 void prvs(){
30     for(int i=0;i<=n;++i)  lk[i]=0;
31     ltp=0;
32     for(int i=0;i<=n;++i){
33         f[i]=0;
34         g[i]=0;
35     }
36 }
37 int main(){
38     while(scanf("%d%d%d",&n,&m,&o)!=EOF){
39         prvs();
40         int l,r;
41         while(m --> 0){
42             scanf("%d%d",&l,&r);
43             ist(r,l);
44         }
45         g[o]=2;
46         for(int i=lk[o];i;i=e[i].nxt)
47             bfs(e[i].y);
48         for(int i=lk[o];i;i=e[i].nxt)
49             f[e[i].y]=1;
50         int cnt=0;
51         for(int i=0;i<=n;++i)if(g[i]==1 && f[i]==1)
52             ++cnt;
53         printf("%d\n",cnt);
54         for(int i=0;i<=n;++i)if(g[i]==1 && f[i]==1)
55             printf("%d\n",i);
56     }
57     return 0;
58 }
View Code

 

posted on 2020-10-19 09:46  cdcq  阅读(210)  评论(0编辑  收藏  举报