二分答案,轉化成求最少的路徑,覆蓋住所有權值$\leq mid$的點。
建立二分圖,若$i$的後繼為$j$,則連邊$i\rightarrow j$,求出最大匹配,則點數減去最大匹配數即為最少需要的路徑數量。
特別地如果某個點$i$的權值$>mid$,則它可以不經過,連邊$i\rightarrow i$表示忽略該點。
因為這是稠密圖,用bitset優化匈牙利演算法即可。
時間複雜度$O(\frac{m^3\log m}{32})$。
#include<cstdio> #include<algorithm> using namespace std; typedef unsigned int U; const int N=505,M=N/32+1; int n,m,tot,k,i,j,x,l,r,mid,ans,v[N],q[N],f[N],cnt;U a[N][M],g[N][M],b[M]; inline void set1(U v[],int x){v[x>>5]|=1U<<(x&31);} inline void flip(U v[],int x){v[x>>5]^=1U<<(x&31);} bool find(int x){ for(int i=0;i<=tot;i++)while(1){ U o=g[x][i]&b[i]; if(!o)break; int y=i<<5|__builtin_ctz(o); flip(b,y); if(!f[y]||find(f[y]))return f[y]=x,1; } return 0; } bool check(int lim){ for(i=1;i<=m;i++)for(j=0;j<=tot;j++)g[i][j]=a[i][j]; for(i=1;i<=m;i++)if(v[i]>lim)set1(g[i],i); for(i=1;i<=m;i++)f[i]=0; cnt=0; for(i=1;i<=m;i++){ for(j=1;j<=m;j++)set1(b,j); if(find(i))cnt++; } return m-cnt<=n; } int main(){ scanf("%d%d",&n,&m);n++;tot=m>>5; for(i=1;i<=m;i++){ scanf("%d%d",&v[i],&k); q[i]=v[i]; while(k--)scanf("%d",&x),set1(a[i],x); } sort(q+1,q+m+1); l=1,r=m; while(l<=r){ mid=(l+r)>>1; if(check(q[mid]))l=(ans=mid)+1;else r=mid-1; } if(ans==m)puts("AK");else printf("%d",q[ans+1]); }