题意:有2n把钥匙和m扇门。这2n把钥匙被分成若干对,每对只能选一把来使用。每扇门可以被指定的两把钥匙打开(只用其中一把)。只有按顺序打开前i-1扇门才能打开第i扇门。问最多可以打开多少扇门。
分析:首先二分答案,然后分析到每扇门就相当于一个or运算,然后建图:
i表示选第i对钥匙的前一把,i+n表示选第i对钥匙的后一把,next(i)表示和钥匙i对应的另一把钥匙。
假设某扇门需要钥匙i和j则连边next(i)->j next(j)->i
然后跑强连通分量。
若有满足的i使得i和next(i)在同一强连通分量,则不满足条件,反之则满足。
代码:
var n,m,l,r,ans,mid,i,tot,sum,d,e:longint; flag:boolean; x,y,p,q,stack,low,dfn,belong,last,num:array[1..3000] of longint; f:array[1..3000] of boolean; side:array[1..100000] of record x,y,next:longint; end; procedure add(x,y:longint); begin inc(e); side[e].x:=x; side[e].y:=y; side[e].next:=last[x]; last[x]:=e; end; function min(x,y:longint):longint; begin if x<y then exit(x) else exit(y); end; procedure dfs(x:longint); var i:longint; begin inc(d); low[x]:=d; dfn[x]:=d; inc(tot); stack[tot]:=x; f[x]:=true; i:=last[x]; while i>0 do with side[i] do begin if dfn[y]=0 then begin dfs(y); low[x]:=min(low[x],low[y]); end else if f[y] then low[x]:=min(low[x],dfn[y]); i:=next; end; if dfn[x]=low[x] then begin inc(sum); repeat i:=stack[tot]; f[i]:=false; dec(tot); belong[i]:=sum; until i=x; end; end; function next(x:longint):longint; begin if x>n then exit(x-n) else exit(x+n); end; begin readln(n,m); while n+m>0 do begin for i:=1 to n do begin readln(x[i],y[i]); inc(x[i]); inc(y[i]); num[x[i]]:=i; num[y[i]]:=i+n; end; for i:=1 to m do begin readln(p[i],q[i]); inc(p[i]); inc(q[i]); p[i]:=num[p[i]]; q[i]:=num[q[i]]; end; l:=0; r:=m; ans:=0; while l<=r do begin mid:=(l+r) div 2; e:=0; fillchar(last,sizeof(last),0); for i:=1 to mid do begin add(next(p[i]),q[i]); add(next(q[i]),p[i]); end; fillchar(low,sizeof(low),0); fillchar(dfn,sizeof(dfn),0); fillchar(f,sizeof(f),false); d:=0; tot:=0; sum:=0; for i:=1 to n*2 do if dfn[i]=0 then dfs(i); flag:=true; for i:=1 to n do if belong[i]=belong[i+n] then begin flag:=false; break; end; if flag then begin if mid>ans then ans:=mid; l:=mid+1; end else r:=mid-1; end; writeln(ans); readln(n,m); end; end.