#4740. 校运会

题目描述

$S$S$Z$X 的校运会又开始了。

高一 $X$ 班的同学们组成了一个由 $n$ 名同学构成的代表队。运动会一共设 $m$ 个运动项目,每名同学只能报名最多一个项目,每个项目每个班级也只能报名最多一名同学。

高一 $X$ 班的体育委员收集了代表队所有名同学的期望参赛项目和预估参赛成绩。预估参赛成绩分为 $K$ 档,从第 $1$ 名到第 $K$ 名。同学们只愿意参加上报的比赛项目。

体育委员把收集的数据交给了你,你需要提供一个报名方案,使得在满足比赛规则和同学要求的情况下,使获得第一名的选手最多。如果两个方案获得第一名的选手一样多,则希望获得第二名的选手最多。以此类推,直到第$K$名。

题解

考虑对一个方案附上权值,方便比较大小,这样的话我们设计1001进制即可。然后上个费用流即可,这里不要求达到最大流!

代码

#include <bits/stdc++.h>
#define I __int128
using namespace std;
const int N=2005,M=30005;
int n,m,k,V[M],hd[N],S,T,W[M],nx[M],t=1,lst[N],pre[N],fl[N],mc[M],s[15];
I vl[15],C[M],d[N];
bool vis[N];
queue<int>q;
void add(int u,int v,int w,I c){
    nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=w;C[t]=c;
}
void ins(int u,int v,int w,I c){
    add(u,v,w,c);add(v,u,0,-c);
}
bool spfa(){
    for (int i=1;i<=T;i++) d[i]=-vl[0],vis[i]=0;
    for (q.push(S),vis[S]=1;!q.empty();){
        int u=q.front();q.pop();vis[u]=0;
        for (int v,i=hd[u];i;i=nx[i]){
            v=V[i];
            if (W[i] && d[v]<d[u]+C[i]){
                d[v]=d[u]+C[i];
                fl[v]=min(fl[u],W[i]);
                pre[v]=u;lst[v]=i;
                if (!vis[v]) q.push(v),vis[v]=1;
            }
        }
    }
    return d[T]!=-vl[0];
}
int main(){
    cin>>n>>m>>k;vl[k]=1;T=n+m+1;fl[0]=1e9;
    for (int i=k-1;~i;i--) vl[i]=vl[i+1]*(min(n,m)+1);
    for (int v,i=1;i<=n;i++){
        scanf("%d",&v);
        for (int x,y;v--;)
            scanf("%d%d",&x,&y),
            ins(i,x+n,1,vl[y]),mc[t^1]=y;
    }
    int v=t;
    for (int i=1;i<=n;i++) ins(S,i,1,0);
    for (int i=1;i<=m;i++) ins(i+n,T,1,0);
    while(spfa()){
        int x=T;
        if (d[T]<0) break;
        while(x!=S){
            W[lst[x]]-=fl[T];
            W[lst[x]^1]+=fl[T];
            x=pre[x];
        }
    }
    for (int i=2;i<=v;i+=2)
        if (!W[i]) s[mc[i]]++;
    for (int i=1;i<=k;i++)
        printf("%d",s[i]),putchar(i<k?' ':'\n');
    return 0;
}

 

posted @ 2020-02-29 21:01  xjqxjq  阅读(140)  评论(0编辑  收藏  举报