P4382 [八省联考2018]劈配 (二分+最大流)

这题还是个思维好题的
我貌似是一个错误的做法水过去了

我们先一问一问的来考虑

首先考虑第一问。

首先,对于每一个导师,由于每一个战队的人员有限制,所以我们将每一个导师的点连向 T T T,流量是战队的人数。

由于存在一个优先级的问题,所以我们不难发现,可以从第一个人开始枚举,依次进行导师的确定。
对于当前的人,我们按照志愿从小到大的顺序来进行连边,记录下来当前志愿有哪些导师,然后连边,如果当前情况下最大流在某一个志愿大于0,那么就表示可以匹配这个志愿,更新 a n s ans ans数组。
(需要注意的是,同一个志愿的导师要一起连边。)
这里为了剪枝,我们需要把那些没有导师匹配的选手的边都删除,也就是直接将 p o i n t point point c n t cnt cnt复原就 o k ok ok

for (int i=1;i<=n;i++)
  	 {  
  	    for (int j=1;j<=m;j++) num[j]=0;
  	 	for (int j=1;j<=m;j++)
  	 	{
  	 		if (!a[i][j]) continue;
  	 		++num[a[i][j]];
  	 		v[a[i][j]][num[a[i][j]]]=j;
  	    }
  	    insert(s,i,1);
  	    for (int j=1;j<=m;j++)
  	    {
  	    	int pre = cnt;
  	    	for (int i=1;i<=t;i++) b[i]=point[i];
  	    	for (int k=1;k<=num[j];++k) 
              insert(i,v[j][k]+n,1);//,cout<<i<<" "<<v[j][k]<<" "<<"****"<<endl;
            int now = dinic();
            if (now==1) 
            {
                ans[i]=j;
                break;
            }
            else
            {
            	cnt=pre;
            	for (int i=1;i<=t;i++) point[i]=b[i];
            }
  	    }
  	    for (int j=1;j<=t;j++) s1[i][j]=point[j];
  	    for (int j=1;j<=cnt;j++) s2[i][j]=nxt[j],s3[i][j]=to[j],s4[i][j]=val[j];
  	    ymh[i]=cnt;
     }

那么这样第一问就解决了,现在考虑第二问,我们会发现,每一个人的需要上升的名次具有二分性,由于我们要求最少的上升名次,所以可以直接二分。
但是(经过测试),我们发现,如果每次二分然后直接暴力建图重新跑,是会 t l e tle tle的,这时候,我们选择的方法是戏省空间换时间,在第一问的时候,没算完一个人就记录一下当前状态的邻接表数组,然后我们对于二分到 k k k这个排名,可以直接将 1 到 k − 1 1到k-1 1k1的边都直接用 f o r for for赋值过来,然后处理当前人,这样就不会$ t l e tle tle

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<set>
#define pb push_back
#define mk make_pair
#define ll long long
#define lson ch[x][0]
#define rson ch[x][1]

using namespace std;

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

const int maxn = 450;
const int maxm = 2e5+1e2;
const int inf = 1e9;
const int N = 80010;

int cnt=1,n,m;
int point[maxn],nxt[maxm],to[maxm],val[maxm];
int h[maxn];
int a[maxn][maxn];
int res[maxn];
int ans[maxn];
int c,t;
int s;
int p[maxn];
int s1[maxn][maxn];
int s2[210][N];
int s3[210][N];
int s4[210][N];
int ymh[maxn];

void addedge(int x,int y,int w)
{
    nxt[++cnt]=point[x];
    to[cnt]=y;
    val[cnt]=w;
    point[x]=cnt;
}

void insert(int x,int y,int w)
{
  addedge(x,y,w);
  addedge(y,x,0);
}

queue<int> q;

bool bfs(int s)
{
    memset(h,-1,sizeof(h));
    h[s]=0;
    q.push(s);
    while (!q.empty())
    {
        int x = q.front();
        q.pop();
        for (int i=point[x];i;i=nxt[i])
        {
            int p = to[i];
            if (val[i]>0 && h[p]==-1)
            {
                h[p]=h[x]+1;
                q.push(p);
            }
        }
    }
    //cout<<"lwk"<<endl;
    if (h[t]==-1) return false;
    return true;
}

int dfs(int x,int low)
{
    if (x==t || low==0) return low;
    int totflow=0;
    for (int i=point[x];i;i=nxt[i])
    {
      int p = to[i];
      if (h[p]==h[x]+1 && val[i]>0)
      {
      	 int tmp = dfs(p,min(low,val[i]));
      	 val[i]-=tmp;
      	 val[i^1]+=tmp;
      	 low-=tmp;
      	 totflow+=tmp;
      	 if (low==0) return totflow;
      }
   }
   if (low>0) h[x]=-1;
   return totflow;
}

int dinic()
{
    int ans=0;
    while (bfs(s)) ans+=dfs(s,inf);
    return ans;
}

int v[maxn][maxn];
int num[maxn];

void init()
{
    cnt=1;
    memset(point,0,sizeof(point));
    memset(a,0,sizeof(a));
    memset(res,0,sizeof(res));
    memset(p,0,sizeof(p));
    memset(ans,0,sizeof(ans));
    memset(num,0,sizeof(num));
    memset(ymh,0,sizeof(ymh));
    memset(s1,0,sizeof(s1));
    memset(s2,0,sizeof(s2));
    memset(s3,0,sizeof(s3));
    memset(s4,0,sizeof(s4));
}

int T;
int b[maxn];

bool check(int x,int st)
{
     cnt=1;
     memset(point,0,sizeof(point));
     int uu = m+1;
     cnt = ymh[x-1];
     for (int i=1;i<=t;i++) point[i]=s1[x-1][i];
     for (int i=2;i<=cnt;i++) nxt[i]=s2[x-1][i],to[i]=s3[x-1][i],val[i]=s4[x-1][i];
     
     int i = st;//cout<<cnt<<" "<<st<<" "<<nxt[2]<<" "<<to[2]<<" "<<point[2]<<endl;
  	 for (int j=1;j<=m;j++) num[j]=0;
  	 for (int j=1;j<=m;j++)
  	 {
  	 	if (!a[i][j]) continue;
  	 	++num[a[i][j]];
  	 	v[a[i][j]][num[a[i][j]]]=j;
  	 }
  	 insert(s,i,1);
  	 for (int j=1;j<=m;j++)
  	 {
  	    int pre = cnt;
  	    for (int i=1;i<=t;i++) b[i]=point[i];
  	    for (int k=1;k<=num[j];++k) 
            insert(i,v[j][k]+n,1);//,cout<<i<<" "<<v[j][k]<<" "<<"****"<<endl;
        int now = dinic();
        if (now==1) 
        {
            uu=j;
            break;
        }
        else
        {
            cnt=pre;
            for (int i=1;i<=t;i++) point[i]=b[i];
        }
     }
     //cout<<uu<<endl;
     if (uu<=p[st]) return true;
     return false;
}
int solve(int now)
{
    int l=1,r=now,ans=0;
    while (l<=r)
    {
        int mid =l+r >> 1;
        if (check(mid,now)) ans=mid,l=mid+1;
        else r=mid-1;
    }
    //cout<<"******"<<endl;
    //check(1,2);
    //cout<<ans<<endl;
    return abs(ans-now);
}
    
int main()
{
  //freopen("yyy.in","r",stdin);
  //freopen("yyy.out","w",stdout);
  T=read(),c=read();
  while (T--)
  {
  	 init();
  	 n=read(),m=read();
  	 s=maxn-8;
  	 t=s+1;
  	 for (int i=1;i<=m;i++) res[i]=read();
  	 
  	 for (int i=1;i<=n;i++) 
  	   for (int j=1;j<=m;j++) 
  	     a[i][j]=read();
  	     
  	 for (int i=1;i<=n;i++) p[i]=read();
  	 
  	 for (int i=1;i<=m;i++) insert(i+n,t,res[i]);//,cout<<res[i]<<"((("<<endl;;
  	 ymh[0]=cnt;
     for (int j=1;j<=t;j++) s1[0][j]=point[j];
  	 for (int j=1;j<=cnt;j++) s2[0][j]=nxt[j],s3[0][j]=to[j],s4[0][j]=val[j];
  	 for (int i=1;i<=n;i++)
  	 {  
  	    for (int j=1;j<=m;j++) num[j]=0;
  	 	for (int j=1;j<=m;j++)
  	 	{
  	 		if (!a[i][j]) continue;
  	 		++num[a[i][j]];
  	 		v[a[i][j]][num[a[i][j]]]=j;
  	    }
  	    insert(s,i,1);
  	    for (int j=1;j<=m;j++)
  	    {
  	    	int pre = cnt;
  	    	for (int i=1;i<=t;i++) b[i]=point[i];
  	    	for (int k=1;k<=num[j];++k) 
              insert(i,v[j][k]+n,1);//,cout<<i<<" "<<v[j][k]<<" "<<"****"<<endl;
            int now = dinic();
            if (now==1) 
            {
                ans[i]=j;
                break;
            }
            else
            {
            	cnt=pre;
            	for (int i=1;i<=t;i++) point[i]=b[i];
            }
  	    }
  	    for (int j=1;j<=t;j++) s1[i][j]=point[j];
  	    for (int j=1;j<=cnt;j++) s2[i][j]=nxt[j],s3[i][j]=to[j],s4[i][j]=val[j];
  	    ymh[i]=cnt;
     }
     //cout<<endl;
     for (int i=1;i<=n;i++) 
     {
     	if (!ans[i]) ans[i]=m+1,cout<<m+1<<" ";
     	else cout<<ans[i]<<" ";
     }
     cout<<"\n";
     for (int i=1;i<=n;i++)
       if (ans[i]>p[i])
         cout<<solve(i)<<" ";
       else cout<<0<<" ";
     cout<<endl;
  }
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值