BZOJ5335: [TJOI2018]智力竞赛
https://lydsy.com/JudgeOnline/problem.php?id=5335
分析:
- 题意有两点需要注意:
-
- 回答过的题目还能再回答一次
-
- 图是个有向无环图(这怎么从题意中看出来?)
- 那么就好做了,二分答案之后转化成最小路径覆盖问题。
- 对于这类问题,我们一般的解法如下:
- 如果不可重复经过点,那么把每个点拆成两个点对于((u,v))的一条边连((u1,v2))。
- 答案等于总点数-最大匹配数。
- 如果可重复经过,那么用闭包传递处理出来两点的可达性再新建出这么若干条边即可转化成上面的模型。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define N 1050
#define M 500050
#define inf 0x3f3f3f3f
const int S=N-1,T=N-2;
int n,m,V[N],G[N][N],id[N];
int head[N],cnt,to[M],nxt[M],flow[M];
inline void add(int u,int v,int f) {
to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; flow[cnt]=f;
to[++cnt]=u; nxt[cnt]=head[v]; head[v]=cnt; flow[cnt]=0;
}
void floyd() {
int i,j,k;
for(k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)G[i][j]|=(G[i][k]&&G[k][j]);
}
inline bool cmp(const int &x,const int &y) {return V[x]<V[y];}
int Q[N],dep[N];
bool bfs() {
int l=0,r=0;
memset(dep,0,sizeof(dep));
Q[r++]=S; dep[S]=1;
while(l<r) {
int x=Q[l++],i;
for(i=head[x];i;i=nxt[i]) if(!dep[to[i]]&&flow[i]) {
dep[to[i]]=dep[x]+1; if(to[i]==T)return 1; Q[r++]=to[i];
}
}return 0;
}
int dfs(int x,int mf) {
if(x==T) return mf;
int nf=0,i;
for(i=head[x];i;i=nxt[i]) if(dep[to[i]]==dep[x]+1&&flow[i]) {
int tmp=dfs(to[i],min(mf-nf,flow[i]));
if(!tmp) dep[to[i]]=0;
nf+=tmp;
flow[i]-=tmp;
flow[i^1]+=tmp;
if(nf==mf) break;
}return nf;
}
int dinic() {
int maxf=0,f=0;
while(bfs()) {
while((f=dfs(S,inf))) maxf+=f;
}return maxf;
}
bool check(int mid) {
if(mid<=n) return 1;
memset(head,0,sizeof(head)); cnt=1;
int i,j;
for(i=1;i<=mid;i++) {
add(S,i,1);
add(i+mid,T,1);
}
for(i=1;i<=mid;i++) for(j=1;j<=mid;j++) {
if(G[id[i]][id[j]]) add(i,j+mid,1);
}
return mid-dinic()<=n;
}
int main() {
scanf("%d%d",&n,&m); n++;
int i,k,x;
for(i=1;i<=m;i++) {
scanf("%d%d",&V[i],&k);
while(k--) {
scanf("%d",&x);
G[i][x]=1;
}
}
floyd();
for(i=1;i<=m;i++) id[i]=i;
sort(id+1,id+m+1,cmp);
int l=1,r=m+1;
while(l<r) {
int mid=(l+r)>>1;
if(check(mid)) l=mid+1;
else r=mid;
}
if(l==m+1) puts("AK");
else printf("%d
",V[l]);
}