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;
}