[TJOI2018]智力竞赛
智力竞赛
题解
挺水的一道题。
题目相当于给出一个DAG,而你每次可以覆盖一条链,总共
n
+
1
n+1
n+1条链。应该不会有人跟我一样想成覆盖它能到达的所有点
一看到最低奖励值最大,应该是很容易想到二分的。我们可以先将小于我们二分值的点的子图建出来,再求出
n
+
1
n+1
n+1条链是否可以完全覆盖这个子图。
由于我们当前每次只会覆盖掉一条链,所以我们这条链开始的位置一定是一个小于目标二分值的点,否则我们完全可以走到一个小于目标二分值的点再去覆盖。
由于需要求出当前DA的最小可交路径覆盖是否小于
n
+
1
n+1
n+1,我们需要求出这个DAG最小可交路径覆盖的值。
DAG的最小路径覆盖可以用二分图来求。我们先将每个点拆成入点和出点两个点,对于一条边,在它起点的入点与终点的出点之间连边。这样得到的图明显是个二分图,跑出这个图的最大独立集,很明显,在最大独立集上的点都是在路径上的点,剩下的没有被选到的点就是DAG上的路径起点的入点与终点的出点。故需要的路径数为
∣
V
∣
−
最
大
独
立
集
|V|-最大独立集
∣V∣−最大独立集。
由于最大独立集二分图的最大匹配,我们可以通过匈牙利来求。
会到原题上,由于我们每次建出的图都是原图的子图,我们需要更改一下我们连边的方式。我们可以先用Floyd跑出原图的联通性(即任意两个点间是否联通),再通过联通性建图跑最大匹配。
这两种方法本质上是一样的,可以论证无法得到比最小路径覆盖的方法得到的独立集更多的独立集数。
总时间复杂度 O ( m 3 ) O\left(m^3\right) O(m3),其实主要是Floyd的时间复杂度。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 505
#define reg register
typedef long long LL;
const int INF=0x7f7f7f7f;
template<typename _T>
inline void read(_T &x){
_T f=1;x=0;char s=getchar();
while('0'>s||'9'<s){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int n,m,val[MAXN],head[MAXN],tot,maxx,sum,cnt,p[MAXN],stak,arr[MAXN];
bool link[MAXN][MAXN],vis[MAXN];
struct edge{int to,nxt;}e[MAXN*MAXN];
inline void addEdge(const int u,const int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
inline bool match(const int x){
vis[x]=1;
for(reg int i=head[x];i;i=e[i].nxt)
if(p[e[i].to]==-1||(!vis[p[e[i].to]]&&match(p[e[i].to])))
return (p[e[i].to]=x,1);
return 0;
}
inline bool sakura(const int mid){
//printf("Let's sakura %d\n",mid);
stak=0;for(reg int i=1;i<=m;++i)if(val[i]<mid)arr[++stak]=i;
cnt=0;for(reg int i=1;i<=stak;++i)head[i]=0,p[i]=-1;tot=0;
for(reg int i=1;i<=stak;++i)for(reg int j=1;j<=stak;++j)if(link[arr[i]][arr[j]])addEdge(i,j);
for(reg int i=1;i<=stak;++i){for(reg int j=1;j<=stak;++j)vis[j]=0;if(match(i))++cnt;}
//printf("Emiya got %d and had %d\n",siz-cnt,n);
return stak-cnt<=n;
}
signed main(){
read(n);read(m);++n;
for(reg int i=1;i<=m;++i){
int siz;read(val[i]);read(siz);maxx=max(val[i],maxx);
for(reg int j=1,v;j<=siz;++j)read(v),link[i][v]=1;
}
for(reg int k=1;k<=m;++k)
for(reg int i=1;i<=m;++i)
for(reg int j=1;j<=m;++j)
link[i][j]|=(link[i][k]&&link[k][j]);
for(reg int i=1;i<=m;++i)head[i]=0;tot=0;
if(sakura(maxx+1)){puts("AK");return 0;}int l=0,r=maxx,ans=0;
while(l<=r){int mid=(l+r)>>1;if(sakura(mid))l=mid+1,ans=mid;else r=mid-1;}
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号