problem大意:给you一个无loop有向graph,求它的key vertex数目。(最近刚过了英语六级,耶,1Y,炫耀~~~~~)
什么是key vertex呢?就是去掉以后从起点到不了终点了的点。跟割点还是有区别的吧?以下就简称key了。
一看就是“哔——”的图论,可能还跟最小割有关系,但构不出图,唉,网络流不熟悉啊。
但想了想么还是可以做的,起点到终点随便找条路,key必都在这条路径上(没有路么就全都算key了)。
然后再开始搜,但是搜到路径上第一个点就不从这个点往下搜,若如此做还是能搜到第一个点后面的点,那这个点就不是key了。反之其即为key。
具体做法:
初始化:
先把路径上的点从0开始标号,不在路径上的点都标为-1。标号对应的是哪条边也记录一下吧。
把限制limit定为1(因为我们要判断路径上第1个点是不是key,所以搜到第1个点就不要再往下搜。而0和最后个点必是key,所以limit从1开始),同时把起点出发的第一条边放入队列。
开始啦:
队列里拿出边,不管怎样把他的下一条边放入队列(我用正向表存的)。
这条边所到的点有三种情况:(当然搜到过的点就不要再搜了)
1、标号小于limit,说明还可以继续搜,把这个点出发的第一条边放入队列。
2、标号等于limit,啥事不干。
3、标号大于limit,那说明标号前面的都不是key了(已经找到的key当然除外),那就把limit定为当前标号(因为我们要看这个点是不是key了嘛,前面的都不用管了),同时别忘了把之前的limit这个点出发的第一条边放入队列(因为第2种情况时没放进去)。
如果标号是路径上最后个点那就break掉吧,这个点无需判断必是key了。
如果队列空了,说明当前的limit对应的点是key,好了,现在要判断下一个点了,那么从这个点出发的边就要加进来了,然后result++,limit++。当然limit对应了路径终点时break掉就好了。
最后把result加上头尾的2就是答案了。
因为每个边只扫描了一遍就是O(E)的复杂度啦。跑了1000多ms。
搞完看discuss说输入就要1s,那跑1s内的那些肯定用了输入外挂了,也猥琐滴用了下,轻松第一啦,唔嘻嘻嘻嘻嘻嘻~~~~~~~~
#include<iostream> #include<queue> #define INF 2100000000 using namespace std; struct Edge{ int u,v,next; }; Edge edge[300000]; int head[100000],en; bool mark[100000]; int N,M,S,E; int f_min(int x,int y){ return x<y?x:y; } void insert(int u,int v){ edge[en].u=u;edge[en].v=v; edge[en].next=head[u]; head[u]=en++; } int Scan(){ int res = 0 , ch ; while( !( ( ch = getchar() ) >= '0' && ch <= '9' ) ); res = ch - '0' ; while( ( ch = getchar() ) >= '0' && ch <= '9' ) res = res * 10 + ( ch - '0' ) ; return res ; } bool get_in(){ if(scanf("%d%d",&N,&M)==EOF)return 0; memset(head,-1,sizeof(head));en=0; int u,v; while(M--){ u=Scan();v=Scan(); insert(u,v); } scanf("%d%d",&S,&E); return 1; } int id[100000],path[100000],pn; int cur[100000]; bool find_path(){ path[0]=S;pn=1; int i,e,u; for(i=0;i<N;i++){ cur[i]=head[i]; mark[i]=0; } mark[S]=1; while(pn){ u=path[pn-1]; for(;cur[u]!=-1;cur[u]=edge[cur[u]].next){ if(!mark[edge[cur[u]].v])break; } e=cur[u]; if(e==-1){ pn--; continue; } mark[edge[e].v]=1; path[pn++]=edge[e].v; if(edge[e].v==E)break; } return pn>0; } int que[300000],qhead,qtail; void limit_search(){ int lim=1; int v,e,res=2; for(int i=0;i<N;i++)mark[i]=0; qhead=qtail=0; que[qtail++]=head[S]; mark[S]=1; while(lim<pn-1){ while(qtail>qhead){ e=que[qhead++]; v=edge[e].v; if(edge[e].next!=-1)que[qtail++]=edge[e].next; if(mark[v])continue; mark[v]=1; if(id[v]<lim){if(head[v]!=-1)que[qtail++]=head[v];} else if(id[v]>lim){ if(head[path[lim]]!=-1)que[qtail++]=head[path[lim]]; lim=id[v]; if(lim==pn-1)break; } } if(lim==pn-1)break; if(head[path[lim]]!=-1)que[qtail++]=head[path[lim]]; res++; lim++; } printf("%d\n",res); } void run(){ int i; for(i=0;i<N;i++)id[i]=-1; if(!find_path()){printf("%d\n",N);return;} for(i=0;i<pn;i++)id[path[i]]=i; limit_search(); } int main(){ while(get_in())run(); return 0; }