JZOJ 5640. 【NOI2018模拟4.9】劈配

31 篇文章 0 订阅
9 篇文章 0 订阅

Description

Description
Description

Input

Input

Output

输出到文件 mentor.out 中。
按顺序输出每组数据的答案。对于每组数据,输出 2 行:
• 第 1 行输出 n 个用空格隔开的正整数,其中第 i 个整数的意义为:
– 在最优的录取方案中,编号为 i 的选手会被该档志愿录取。
– 特别地,如果该选手出局,则这个数为 m + 1。
• 第 2 行输出 n 个用空格隔开的非负整数,其中第 i 个整数的意义为:
– 使编号为 i 的选手不沮丧,最少需要让他上升的排名数。
– 特别地,如果该选手一定会沮丧,则这个数为 i。

Sample Input

输入1:

3 5
2 2
1 1
2 2
1 2
1 1
2 2
1 1
1 2
1 2
2 1
2 2
1 1
0 1
0 1
2 2

输入2:

1 5
4 3
2 1 1
3 1 3
0 0 1
3 1 2
2 3 1
2 3 3 3

Sample Output

输出1:

2 1
1 0
1 2
0 1
1 3
0 1

【样例 1 解释】

三组数据分别与【题目描述】中的三个表格对应。
对于第 1 组数据:由于选手 1 没有填写第一志愿,所以他一定无法被第一志愿录
取,也就一定会沮丧。选手 2 按原排名就不沮丧,因此他不需要提升排名。
对于第 2 组和第 3 组数据:1 号选手都不需要提升排名。而希望被第一志愿录取的
2 号选手都必须升到第 1 名才能如愿。

输出2:

1 1 3 2
0 0 0 0

【样例 2 解释】

1 号选手的第一志愿只填写了 2 号导师,因此 1 号选手必定被 2 号导师录取。
2 号选手的第一志愿只填写了 3 号导师,因此 2 号选手必定被 3 号导师录取。
由于 2,3 号导师均满员,且 3,4 号选手均填写了 1 号导师,因此它们都会被 1 号导
师录取。
所以 1,2 号选手均被第 1 志愿录取,3 号选手被第 3 志愿录取,4 号选手被第 2 志
愿录取。
由于他们都如愿以偿了,所以他们都不需要提升名次。

Data Constraint

Data Constraint

Solution

  • 题面真的很长……

  • 一看数据范围 n,m200 ,嗯,网络流。

  • 由于每次都是最优方案,所以最终每个人选的志愿都是唯一固定的。

  • 我们只需要知道他们到底选了哪个志愿,不需要知道具体选了哪个老师。

  • 题目有两个问,考虑先做第一个问。

  • 假设前 i1 个人已经处理完了,现在要求出第 i 个人的最优志愿 f[i]

  • 考虑 二分 这第 i 个人最好能选第几志愿,设为 mid ,接着判断 i 能否选志愿 mid

  • 具体方法:建源汇点 s,t s 1 i 各连一条容量为 1 的边,

  • 之后每位导师 j t 各连一条容量为 b[j] 的边。

  • 然后 1 i1 按照之前处理好的志愿向对应的第 f[] 志愿的导师连容量为 1 的边。

  • 最后 i 向其第 mid 志愿的每个老师都连一条容量为 1 的边即可。

  • 每次暴力建图,跑一遍最大流,算出最大流量 sum ,这个流量就代表着有 sum 个人选了志愿。

  • 那么如何判断二分的这个 mid 可行呢?

  • 我们可以记录一个 pre[i] ,表示前 i 个人按照最优方案执行后 没出局的人数

  • 如果满足:pre[i1]+1=sum 即为满足(即第 i 个人成功选上了)。

  • 这样我们就二分出了最优的 mid ,更新 f[i] 即可。

  • 于是我们就求出了第一问的答案 f[i] ,先算算时间复杂度:

  • 先枚举每个人 i O(N),在二分选志愿 O(log N) ,做网络流复杂度设为 C

  • 那么总时间复杂度就是 O(NC logN) ,额,能过。

  • 那我们愉快地再来做第二问。

  • 仍然是考虑二分,对于第 i 个人,二分他要到哪个位置,设为 mid

  • 这是我们还是可以利用之前算出的 f[i] :将 1 mid1 都按照 f 的志愿连边(同上)。

  • 源汇点 s,t 的连边方式也一样,不同的是点 mid 的连边。

  • 由于我们只需要判断位置 mid 对于第 i 个人是否可行(不需要具体求出其选什么志愿),

  • 所以我们直接将 mid 1 s[i] 各连一条容量为 1 的边,表示在其中任选一个志愿即可。

  • 判断也和之前一样,满足:pre[mid1]+1=sum 即为符合,更新答案即可。

  • 分析一下复杂度,枚举每个人 O(N) ,二分选在哪 O(log N) ,之后又做网络流 C

  • 复杂度还是 O(NC logN) !于是总时间复杂度就是 O(NC logN) ,能过本题。

Code

#include<cstdio>
#include<cstring>
#include<vector>
#include<cctype>
using namespace std;
const int N=205,M=N<<1,inf=1e9;
int n,m,s,t,tot;
int first[M],nex[M*M],en[M*M],w[M*M];
int b[N],ss[N],f[N],pre[N];
int dis[M],gap[M],cur[M];
vector<int>g[N][N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline void write(int x)
{
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline int max(int x,int y)
{
    return x>y?x:y;
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
inline void ins(int x,int y,int z)
{
    nex[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
    w[tot]=z;
}
inline void insert(int x,int y,int z)
{
    ins(x,y,z),ins(y,x,0);
}
int sap(int x,int y)
{
    if(x==t) return y;
    int use=0;
    for(int i=cur[x];i;i=nex[i])
        if(w[i] && dis[x]==dis[en[i]]+1)
        {
            cur[x]=i;
            int num=sap(en[i],min(w[i],y-use));
            w[i]-=num,w[i^1]+=num,use+=num;
            if(use==y || dis[s]>t) return use;
        }
    cur[x]=first[x];
    if(!--gap[dis[x]]) dis[s]=t+1;
    gap[++dis[x]]++;
    return use;
}
inline bool check(int ii,int pos)
{
    tot=1;
    memset(first,0,sizeof(first));
    for(int i=1;i<=ii;i++) insert(s,i,1);
    for(int i=1;i<=m;i++) insert(n+i,t,b[i]);
    for(int i=1;i<ii;i++)
        if(f[i]<=m)
            for(int j=0;j<g[i][f[i]].size();j++) insert(i,n+g[i][f[i]][j],1);
    for(int j=1;j<=pos;j++)
        for(int i=0;i<g[ii][j].size();i++) insert(ii,n+g[ii][j][i],1);
    for(int i=1;i<=t;i++) cur[i]=first[i];
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    gap[0]=t;
    int sum=0;
    while(dis[s]<=t) sum+=sap(s,inf);
    return sum==pre[ii-1]+1;
}
inline bool check1(int ii,int nn)
{
    tot=1;
    memset(first,0,sizeof(first));
    for(int i=1;i<nn;i++) insert(s,i,1);
    insert(s,ii,1);
    for(int i=1;i<=m;i++) insert(n+i,t,b[i]);
    for(int i=1;i<nn;i++)
        if(f[i]<=m)
            for(int j=0;j<g[i][f[i]].size();j++) insert(i,n+g[i][f[i]][j],1);
    for(int i=1;i<=ss[ii];i++)
        for(int j=0;j<g[ii][i].size();j++) insert(ii,n+g[ii][i][j],1);
    for(int i=1;i<=t;i++) cur[i]=first[i];
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    gap[0]=t;
    int sum=0;
    while(dis[s]<=t) sum+=sap(s,inf);
    return sum==pre[nn-1]+1;
}
int main()
{
    int T=read(),c=read();
    while(T--)
    {
        n=read(),m=read();
        for(int i=1;i<=m;i++) b[i]=read();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++) g[i][j].clear();
            for(int j=1,x;j<=m;j++)
                if(x=read()) g[i][x].push_back(j);
        }
        for(int i=1;i<=n;i++) ss[i]=read();
        s=n+m+1,t=n+m+2;
        for(int i=1;i<=n;i++)
        {
            int l=1,r=m;
            f[i]=m+1;
            while(l<=r)
            {
                int mid=l+r>>1;
                if(check(i,mid))
                {
                    f[i]=mid;
                    r=mid-1;
                }else l=mid+1;
            }
            write(f[i]),putchar(' ');
            pre[i]=pre[i-1]+(f[i]<=m);
        }
        putchar('\n');
        for(int i=1;i<=n;i++)
        {
            if(f[i]<=ss[i])
            {
                putchar('0'),putchar(' ');
                continue;
            }
            int l=1,r=i-1,pos=0;
            while(l<=r)
            {
                int mid=l+r>>1;
                if(check1(i,mid))
                {
                    pos=mid;
                    l=mid+1;
                }else r=mid-1;
            }
            write(i-pos),putchar(' ');
        }
        putchar('\n');
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值