题解
- 我们把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,&m);
37 gets(s);
38 greedy();
39 for (int i=0;i<m;i++) printf("%d ",fen[i]); printf("
");
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 }