这个题其实就是2-sat,不过似乎题中的数据比所给要大,我开小了先TLE,然后WA,再然后开到6000就AC了~
思路:1、首先对于2个钥匙只能用一把,那么建边。2、对于2把锁不能一把都不开,建边。然后二分跑2-Sat
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<stack> using namespace std; const int maxn=6e3; const int maxm=maxn*20; int e,head[maxn],nxt[maxm],pnt[maxm],dfn[maxn],low[maxn],sccno[maxn],dfs_clock,scc_no; int n,m,x[maxn],y[maxn],doorx[maxn],doory[maxn]; stack<int> s; void AddEdge(int u,int v) { pnt[e]=v;nxt[e]=head[u];head[u]=e++; } void Build(int s) { e=0; memset(head,-1,sizeof(head)); for(int i=0;i<n;i++) { AddEdge(x[i],y[i]+2*n); AddEdge(y[i],x[i]+2*n); } for(int i=0;i<s;i++) { AddEdge(doorx[i]+2*n,doory[i]); AddEdge(doory[i]+2*n,doorx[i]); } } int Tarjan(int u) { dfn[u]=low[u]=++dfs_clock; s.push(u); for(int i=head[u];i!=-1;i=nxt[i]) { if(!dfn[pnt[i]]) low[u]=min(low[u],Tarjan(pnt[i])); else if(!sccno[pnt[i]]) low[u]=min(low[u],dfn[pnt[i]]); } if(low[u]==dfn[u]) { scc_no++; for(;;) { int x=s.top();s.pop(); sccno[x]=scc_no; if(x==u) break; } } return low[u]; } bool check(int mid) { while(!s.empty()) s.pop(); Build(mid); dfs_clock=scc_no=0; memset(dfn,0,sizeof(dfn)); memset(sccno,0,sizeof(sccno)); for(int i=0;i<4*n;i++) if(!dfn[i]) Tarjan(i); for(int i=0;i<2*n;i++) if(sccno[i]==sccno[i+2*n]) return false; return true; } void solve() { int l=0,r=m,ans=0; while(l<=r) { int mid=(l+r)>>1; if(check(mid)) { ans=mid; l=mid+1; } else r=mid-1; } printf("%d\n",ans); } int main() { while(scanf("%d%d",&n,&m)&&(n+m)) { for(int i=0;i<n;i++) scanf("%d%d",&x[i],&y[i]); for(int i=0;i<m;i++) scanf("%d%d",&doorx[i],&doory[i]); solve(); } return 0; }