您的位置:首页 > 其它

BZOJ5335 : [TJOI2018]智力竞赛

2018-08-26 04:06 375 查看

二分答案,转化成求最少的路径,覆盖住所有权值$\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
,q
,f
,cnt;U a
[M],g
[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]);
}

  

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: