luogu P4382 [九省联考2018]劈配

luogu

我记得我第一次做这道题的时候屁都不会qwq

先考虑第一问,暴力是依次枚举每个人,然后从高到低枚举志愿,枚举导师,能选就选.但是可以发现前面的人选的导师可能会导致后面的人本来可以选到这个志愿,但是不能选.这个问题是不是有点眼熟?你可以理解成二分图匹配问题,就是对于每个人,枚举志愿,然后把这个志愿对应的一些导师连边,如果跑匹配能跑出来那么他就是这个志愿,否则撤销刚才的操作

第二问类似,暴力枚举某个人到了前面的哪一名,然后把前面其他人在第一问中的方案的图先建好,然后把对应志愿\(\le s_i\)的导师加入图,如果有那个人能匹配上那么就可以前进到这一名,还要记得撤销.做的时候可以先枚举后面的人到的名次,然后枚举人,这样前面的人的图可以快速建出来.每个人的答案是当前名次-最大的合法名次

实现用的是网络流,因为比较方便退流以及撤销边,开个栈栈序撤销.注意及时撤销

// luogu-judger-enable-o2
//qwqwq
#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=200+10,M=3e5+10;
const db eps=1e-4;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int to[M],nt[M],c[M],hd[N<<1],tot=1;
void add(int x,int y,int z)
{
    ++tot,to[tot]=y,nt[tot]=hd[x],c[tot]=z,hd[x]=tot;
    ++tot,to[tot]=x,nt[tot]=hd[y],c[tot]=0,hd[y]=tot;
}
void wd(){hd[to[tot^1]]=nt[tot],--tot;}
int stk[M<<3][2],tp;
void wdflow()
{
    while(tp)
    {
        int i=stk[tp][0],dt=stk[tp][1];
        c[i]+=dt,c[i^1]-=dt;
        --tp;
    }
}
int ps,pt,lv[N<<1];
queue<int> q;
bool bfs()
{
    for(int i=0;i<=pt;++i) lv[i]=0;
    lv[ps]=1,q.push(ps);
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        for(int i=hd[x];i;i=nt[i])
        {
            int y=to[i];
            if(c[i]>0&&!lv[y])
            {
                lv[y]=lv[x]+1;
                q.push(y);
            }
        }
    }
    return lv[pt];
}
int dfs(int x,int fw)
{
    if(x==pt) return fw;
    int an=0;
    for(int i=hd[x];i;i=nt[i])
    {
        int y=to[i];
        if(c[i]>0&&lv[y]==lv[x]+1)
        {
            int dt=dfs(y,min(fw,c[i]));
            stk[++tp][0]=i,stk[tp][1]=dt;
            c[i]-=dt,c[i^1]+=dt;
            fw-=dt,an+=dt;
            if(!fw) break;
        }
    }
    return an;
}
int dinic()
{
    int an=0,dt;
    while(bfs())
        while((dt=dfs(ps,2333333)))
            an+=dt;
    return an;
}
int n,m,an[N],a2[N],b[N],s[N];
vector<int> dls[N][N];
vector<int>::iterator it;

int main()
{
    int T=rd();
    rd();
    while(T--)
    {
        n=rd(),m=rd();
        ps=0,pt=n+m+1;
        for(int i=0;i<=pt;++i) hd[i]=0;
        tot=1;
        for(int i=1;i<=n;++i)
        {
            add(ps,i+m,1);
            for(int j=1;j<=m+1;++j) dls[i][j].clear();
        }
        for(int i=1;i<=m;++i) add(i,pt,b[i]=rd());
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
                dls[i][rd()].push_back(j);
        for(int i=1;i<=n;++i)
        {
            an[i]=m+1;
            for(int j=1;j<=m;++j)
            {
                if(!dls[i][j].size()) continue;
                int latt=tot;
                for(it=dls[i][j].begin();it!=dls[i][j].end();++it)
                    add(i+m,*it,1);
                tp=0;
                if(dinic()){an[i]=j;break;}
                wdflow();
                while(tot>latt) wd();
            }
            printf("%d ",an[i]);
        }
        puts("");
        for(int i=1;i<=n;++i) s[i]=rd();
        for(int i=0;i<=pt;++i) hd[i]=0;
        tot=1;
        for(int i=1;i<=n;++i) add(ps,i+m,1),a2[i]=0;
        for(int i=1;i<=m;++i) add(i,pt,b[i]);
        for(int i=1;i<=n;++i)
        {
            for(int j=i;j<=n;++j)
            {
                if(an[j]<=s[j]){a2[j]=j;continue;}
                int latt=tot;
                for(int k=1;k<=s[j];++k)
                {
                    if(!dls[j][k].size()) continue;
                    for(it=dls[j][k].begin();it!=dls[j][k].end();++it)
                        add(j+m,*it,1);
                }
                tp=0;
                if(dinic()) a2[j]=i;
                wdflow();
                while(tot>latt) wd();
            }
            for(it=dls[i][an[i]].begin();it!=dls[i][an[i]].end();++it)
                add(i+m,*it,1);
            tp=0,dinic();
        }
        for(int i=1;i<=n;++i) printf("%d ",i-a2[i]);
        puts("");
    }
    return 0; 
}

转载于:https://www.cnblogs.com/smyjr/p/11479504.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值