[贪心][前缀和] JZOJ P1795 教主的别墅

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

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]大。

题解

  • 我们把0当成-1做,跑一边前缀和
  • 用zero记录下有多少个位置为0
  • 如果zero比分组的组数多而且前缀和最后不为0,将x定为0(后面会解释x是什么)
  • 否则,将x定为abs(abs(sum[n-1])-1)/m+1(也就是将sum平均分到每一组的差值,向上取整)
  • 然后我们就可以模拟了,如果当前分了cnt>=m-1,将后面所有的数塞到第m组
  • 如果abs(sum[n-1]-sum[i])/(m-cnt-1)<=x,也就是将i处断后,后面的平均差值小于x,那当然是可以断的
  • 那么怎么满足字典序最小的条件呢?
  • 当然是有的取尽量先取
  • 就字典序最大时,将数组反过来再跑一遍,从大到小输出

代码

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 using namespace std;
 6 int n,m,sum[5000010],ans,fen[5000010];
 7 char s[5000010];
 8 int abs(int x){return x>0?x:-x;}
 9 void greedy()
10 {
11     int cnt=0,num=-1,zero=0,k=0;
12     memset(sum,0,sizeof(sum));
13     for (int i=0;i<=n-1;i++)
14     {
15         if (s[i]==‘1‘) k++; else k--;
16         sum[i]=k;
17         if (!k) zero++;
18     }
19     if (!sum[n-1]&&zero>=m) ans=0; else ans=abs(abs(sum[n-1])-1)/m+1;
20     for (int i=0;i<=n-1;i++)
21     {
22         if (cnt>=m-1)
23         {
24             fen[cnt++]=n-i;
25             break;
26         }
27         if (abs(sum[n-1]-sum[i])<=ans*(m-cnt-1))
28         {
29             fen[cnt++]=i-num;
30             num=i;
31         }
32     }
33 }
34 int main()
35 {
36     scanf("%d%d\n",&n,&m);
37     gets(s);
38     greedy();
39     for (int i=0;i<m;i++) printf("%d ",fen[i]);  printf("\n");
40     reverse(&s[0],&s[n]);
41     greedy();
42     for (int i=0;i<m;i++) printf("%d ",fen[m-i-1]);
43     return 0;
44 } 

原文地址:https://www.cnblogs.com/Comfortable/p/8428199.html

时间: 2024-10-10 09:33:04

[贪心][前缀和] JZOJ P1795 教主的别墅的相关文章

[贪心][前缀和] Jzoj P4256 平均数

Description 给出包含一个N个整数的数组A.找出一段长度至少为K的连续序列,最大化它的平均值.请注意:一段子序列的平均值是子序列中所有数的和除以它的长度. Input 第一行包含两个整数N(1<=N<=300000),K(1<=K<=N). 第二行包含N个整数,代表数组A,1<=ai<=10^6. Output 一行一个实数,代表最大的平均值.允许在0.001以内的绝对误差. Sample Input 输入1:4 11 2 3 4输入2:4 22 4 3 4输

[二分][排序] JZOJ P1792 教主的花园

Description [问题背景] LHX教主最近总困扰于前来膜拜他的人太多了,所以他给他的花园加上了一道屏障. [问题描述] 可以把教主的花园附近区域抽像成一个正方形网格组成的网络,每个网格都对应了一个坐标(均为整数,有可能为负),若两个网格(x1, y1),(x2, y2)有|x1 – x2| + |y1 – y2| = 1,则说这两个网格是相邻的,否则不是相邻的. 教主在y = 0处整条直线上的网格设置了一道屏障,即所有坐标为(x, 0)的网格.当然,他还要解决他自己与内部人员的进出问题

[平衡规划][模拟][前缀和] Jzoj P4724 斐波那契

Description DJL为了避免成为一只咸鱼,来找czgj学习Fibonacci数列.通过czgj的谆谆教导,DJL明白了Fibonacci数列是这样定义的:F(1)=1;F(2)=1;F(n)=F(n-1)+F(n-2)(n>2)Czgj深谙熟能生巧的道理,于是他给了DJL一个数列,并安排了如下的训练计划:1.“1 L r”,表示给ai 加上F(i-L+1) ,其中L<=i<=r :2.“2 L r”,表示询问 的值.DJL经过长时间的学习,感觉身体被掏空,他希望你能帮他解决这个

Codeforces 578B Or Game (前缀和 + 贪心)

Codeforces Round #320 (Div. 1) [Bayan Thanks-Round] 题目链接:B. "Or" Game You are given \(n\) numbers \(a_1,?a_2,?...,?a_n\). You can perform at most \(k\) operations. For each operation you can multiply one of the numbers by \(x\). We want to make

【jzoj6.24模拟B】

这场真是无聊,搬远古原题-- xjb做了做,(居然没AK真是身败名裂) A.教主的花园 答案明显具有可二分性,二分答案判定下就行. #include<bits/stdc++.h> #define N 100010 using namespace std; int n,m,a[N]; inline int check(int x){ int l=1,r=n;int ret=n; while(l<=r){ int mid=(l+r)>>1; if(a[mid]<x)l=mi

Codeforces Round #320 (Div. 2) &quot;Or&quot; Game(好题,贪心/位运算/前缀后缀或)

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 using namespace std; 6 typedef long long ll; 7 /* 8 n个数,你最多有k次操作,每次操作可以选择一个数乘以x,问所有数或(|)的最大值 9 贪心思路:选一个数进行k此乘以x操作; 因为x>=2 10 111 ---> 1111 11

贪心:字典树openjudge1799-最短前缀

描述 一个字符串的前缀是从该字符串的第一个字符起始的一个子串.例如 "carbon"的字串是: "c", "ca", "car", "carb", "carbo", 和 "carbon".注意到这里我们不认为空串是字串, 但是每个非空串是它自身的字串. 我们现在希望能用前缀来缩略的表示单词.例如, "carbohydrate" 通常用"c

算法初级面试题07——前缀树应用、介绍和证明贪心策略、拼接字符串得到最低字典序、切金条问题、项目收益最大化问题、随时取中位数、宣讲会安排

第六课主要介绍图,不经常考,故今天先讲第七课的内容,介绍比较常考的树和贪心算法 介绍前缀树 何为前缀树? 如何生成前缀树? 可以查有多少个字符串以"be"为前缀. 如果要判断有没有"be"这个节点,每个节点上加上一个数据项,有多少个字符串以当前节点结尾的(可以查加了多少次特定字符串). 给一个字符串.返回多少个字符串以这个为前缀. 再加一个数据项,记录该节点被划过多少次. 大概实现: 删除逻辑: 根据path是否变为0,来判断是否继续往下删. 可以解决以下问题: 一

数据结构与算法——前缀树和贪心算法(1)

介绍前缀树 何为前缀树?如何生成前缀树? 例子:一个字符串类型的数组arrl,另一个字符串类型的数组arr2.arr2中有哪些字符,是arr 1中 出现的?请打印.arr2中有哪些字符,是作为arr 1中某个字符串前缀出现的?请打印.arr2 中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印arr2中出现次数最大的前缀. public class TrieTree { public static class TrieNode { public int path; public int