教主的别墅

23 篇文章 0 订阅

Description

【题目背景】

  LHX教主身为宇宙第一富翁,拥有一栋富丽堂皇的别墅,由于别墅实在太大了,于是教主雇佣了许许多多的人来负责别墅的卫生工作,我们不妨称这些人为LHXee。

【题目描述】

  教主一共雇佣了N个LHXee,这些LHXee有男有女。
  教主的大别墅一共有M个房间,现在所有的LHXee在教主面前排成了一排。教主要把N个LHXee分成恰好M个部分,每个部分在队列中都是连续的一段,然后分别去打扫M个房间。
  教主身为全世界知识最渊博的人,他当然知道男女搭配干活不累的道理,以及狼多羊少,羊多狼少的危害之大。所以教主希望一个分配方式,使得所有小组男女个数差的最大值最小。
  教主还希望你输出从左到右,每个组的人数。
  如果有多种人数组合都能达到最优值,教主希望你分别告诉他这些方案中字典序最小和最大的方案。换句话说,你需要找到两种方案,这两种方案满足所有组男女个数差最大值最小的前提下,第一种方案(字典序最小)要越靠前的组人数越少,也就是让第一个小组人尽量少,并且在第一个小组人尽量少的前提下,让第二个小组的人尽量少,依此类推;第二种方案(字典序最大)则要让越靠前的组人数越多。

Input

  输入的第1行为两个正整数N与M,用空格分隔。
  第2行包含一个长度为N的串,仅由字符组成,第i 个字符为0表示在这个位置上的LHXee为女生,若为1则为男生。

Output

  输出文件包含两行,每行M个正整数,正整数之间用空格隔开,行末无多余空格。这M个正整数从左到右描述了你所分的每个组的人数。
  第1行为字典序最小的方案,第2行为字典序最大的方案。

Sample Input

8 3
11001100

Sample Output

1 2 5
5 2 1

Data Constraint

Hint

【样例说明】

  字典序最小的方案按1, 10, 01100分组,每组男女个数差的最大值为1,为最小。
  字典序最大的方案按11001, 10, 0分组。

【数据规模】

  对于40%的数据,有N ≤ 100;
  对于50%的数据,有N ≤ 1000;
  对于65%的数据,有N ≤ 100000;
  对于100%的数据,有N ≤ 5000000,M ≤ N且M ≤ 100000。

【提示】

关于字典序:
比较S1[N]与S2[N]的字典序大小,可以找到S1[N]与S2[N]中第1个不相同数字S1[i]与S2[i](即有对于所有1≤k<i,都有S1[k] =S2[k],但S1[i]≠S2[i])。如果S1[i]<S2[i],那么说S1[N]字典序比S2[N]小,否则说S1[N]字典序比S2[N]大。

思路

一个超神奇的贪心。

先做一遍前缀和a
if (!a[n-1]&&sum>=m) ans=0; else ans=abs(abs(a[n-1])-1)/m+1;
sum为数组a中为零的个数。
输出时线性扫过,满足切掉的部分<=ans,余剩的满足<=ans/(t-i+1)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[5500000];
int a[5500000],f[5500000],n,m,ans;
void doit()
{
    int cnt=0,num=-1,sum=0,k=0;
    memset(a,0,sizeof(a));
    for (int i=0;i<=n-1;i++)
    {
        if (s[i]=='1') k++; else k--;
        a[i]=k;
        if (!k) sum++;
    }
    if (!a[n-1]&&sum>=m) ans=0; else ans=abs(abs(a[n-1])-1)/m+1;
    for (int i=0;i<=n-1;i++)
    {
        if (cnt>=m-1)
        {
            f[cnt++]=n-i;
            break;
        }
        if (abs(a[n-1]-a[i])<=ans*(m-cnt-1))
        {
            f[cnt++]=i-num;
            num=i;
        }
    }
}
int main()
{  
    scanf("%d%d\n",&n,&m);

    gets(s); doit();
    for(int i=0; i<m; i++) printf("%d ",f[i]);  printf("\n");

    reverse(&s[0],&s[n]); doit();
    for(int i=0; i<m; i++) printf("%d ",f[m-i-1]);  
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值