纪中国庆10.5做题小结

纪中国庆10.5做题小结

  • T1:教主的花园
  • T2:教主泡嫦娥
  • T3:保镖排队
  • T4:教主的别墅

T1:教主的花园

Description
【问题背景】
  LHX教主最近总困扰于前来膜拜他的人太多了,所以他给他的花园加上了一道屏障。
【问题描述】
  可以把教主的花园附近区域抽像成一个正方形网格组成的网络,每个网格都对应了一个坐标(均为整数,有可能为负),若两个网格(x1, y1),(x2, y2)有|x1 – x2| + |y1 – y2| = 1,则说这两个网格是相邻的,否则不是相邻的。
  教主在y = 0处整条直线上的网格设置了一道屏障,即所有坐标为(x, 0)的网格。当然,他还要解决他自己与内部人员的进出问题,这样教主设置了N个入口a1, a2, …, aN可供进出,即对于y = 0上的所有网格,只有 (a1, 0),(a2, 0), ……, (aN, 0) 可以通过,之外的所有纵坐标为0的网格均不能通过,而对于(x, y)有y不为0的网格可以认为是随意通过的。
  现在教主想知道,给定M个点对(x1, y1),(x2, y2),并且这些点均不在屏障上,询问从一个点走到另一个点最短距离是多少,每次只能从一个格子走到相邻的格子。

Input
  输入的第1行为一个正整数N,为屏障上入口的个数。
  第2行有N个整数,a1, a2, …, aN,之间用空格隔开,为这N个入口的横坐标。
  第3行为一个正整数M,表示了M个询问。
  接下来M行,每行4个整数x1, y1, x2, y2,有y1与y2均不等于0,表示了一个询问从(x1, y1)到(x2, y2)的最短路。

Output
  输出共包含m行,第i行对于第i个询问输出从(x1, y1)到(x2, y2)的最短路距离是多少。

Sample Input

2
2 -1
2
0 1 0 -1
1 1 2 2

Sample Output

4
2

Hint
【数据规模】
  对于20%的数据,有n,m≤10,ai,xi,yi绝对值不超过100;
  对于40%的数据,有n,m≤100,ai,xi,yi绝对值不超过1000;
  对于60%的数据,有n,m≤1000,ai,xi,yi绝对值不超过100000;
  对于100%的数据,有n,m≤100000,ai,xi,yi绝对值不超过100000000。

简要思路:这题其实就是一个二分找点的题目。首先,对于两个在 y y y轴同侧的点,只要直接计算曼哈顿距离即可,我们要考虑的只是在 y y y轴异侧的两个点。在输入开头 n n n个入口的坐标后,先从小到大排个序,然后对于输入的两个点的 x x x坐标放在存贮入口坐标的数组中二分查找。
如果当前数组中 m i d mid mid下标对应的数小于当前两个点的 x x x坐标,那么 l = m i d + 1 l = mid + 1 l=mid+1,如果大于当前两个点的 x x x坐标,那么 r = m i d − 1 r = mid - 1 r=mid1,二分时记录当前数组中 m i d mid mid下标对应的点与那两个点的最小 x x x轴距离,答案为 2 ∗ 最 小 x 轴 距 离 + 两 点 曼 哈 顿 距 离 2 * 最小x轴距离 + 两点曼哈顿距离 2x+;如果存在一个数在两点 x x x轴中间,不难发现经过该点通往那两点路径长度与两点之间曼哈顿距离相等。

#include 
#include 
#include 
#include 
#define N 100005
#define inf 2000000000
using namespace std;
int a[N];
int n , m , minn;
int x1 , x2 , y1 , y2;
template < typename T >
inline void read( T & res ) {
	res = 0;
	T pd = 1;
	char aa = getchar();
	while ( aa < '0' || aa > '9' ) {
		if ( aa == '-' ) {
			pd = -pd;
		}
		aa = getchar();
	}
	while ( aa >= '0' && aa <= '9' ) {
		res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
		aa = getchar();
	}
	res *= pd;
	return;
}
inline int abs( int cur ) {
	if ( cur >= 0 ) {
		return cur;
	} else {
		return -cur;
	}
}
inline bool erfen() {
	int l = 1 , r = n;
	int mid;
	while ( l <= r ) {
		mid = ( l + r ) >> 1;
		if ( ( a[mid] >= x1 && a[mid] <= x2 ) || ( a[mid] <= x1 && a[mid] >= x2 ) ) {
			return 1;
		}
		if ( a[mid] < x1 && a[mid] < x2 ) {
			if ( abs( a[mid] - x1 ) < minn ) {
				minn = abs( a[mid] - x1 );
			}
			if ( abs( a[mid] - x2 ) < minn ) {
				minn = abs( a[mid] - x2 );
			}
			l = mid + 1;
		}
		if ( a[mid] > x1 && a[mid] > x2 ) {
			if ( abs( a[mid] - x1 ) < minn ) {
				minn = abs( a[mid] - x1 );
			}
			if ( abs( a[mid] - x2 ) < minn ) {
				minn = abs( a[mid] - x2 );
			}
			r = mid - 1;
		}
	}
	return 0;
}
int main () {
	read(n);
	for ( int i = 1 ; i <= n ; ++i ) {
		read(a[i]);
	}
	sort( a + 1 , a + 1 + n );
	read(m);
	for ( int i = 1 ; i <= m ; ++i ) {
		read(x1);
		read(y1);
		read(x2);
		read(y2);
		if ( ( y1 > 0 && y2 > 0 ) || ( y1 < 0 && y2 < 0 ) ) {
			printf("%d\n",(abs( x1 - x2 ) + abs( y1 - y2 )));
			continue;
		}
		int ans = 0;
		ans += abs( y1 - y2 ) + abs( x1 - x2 );
		minn = inf;
		bool flag = erfen();
		if ( flag ) {
			printf("%d\n",ans);
		} else {
			printf("%d\n",ans + minn * 2);
		}
	}
	return 0;
}

T2:教主泡嫦娥

Description
【问题背景】
  2012年12月21日下午3点14分35秒,全世界各国的总统以及领导人都已经汇聚在中国的方舟上。
  但也有很多百姓平民想搭乘方舟,毕竟他们不想就这么离开世界,所以他们决定要么登上方舟,要么毁掉方舟。
  LHX教主听说了这件事之后,果断扔掉了手中的船票。在地球即将毁灭的那一霎那,教主自制了一个小型火箭,奔向了月球……
  教主登上月球之后才发现,他的女朋友忘记带到月球了,为此他哭了一个月。
  但细心的教主立马想起了小学学过的一篇课文,叫做《嫦娥奔月》,于是教主决定,让嫦娥做自己的新任女友。
【题目描述】
  教主拿出他最新研制的LHX(Let’s be Happy Xixi*__*)卫星定位系统,轻松地定位到了广寒宫的位置。
  见到嫦娥之后,教主用温柔而犀利的目光瞬间迷倒了嫦娥,但嫦娥也想考验一下教主。
  嫦娥对教主说:“看到那边的环形山了么?你从上面那个环走一圈我就答应你~”
  教主用LHX卫星定位系统查看了环形山的地形,环形山上一共有N个可以识别的落脚点,以顺时针1~N编号。每个落脚点都有一个海拔,相邻的落脚点海拔不同(第1个和第N个相邻)。
  教主可以选择从任意一个落脚点开始,顺时针或者逆时针走,每次走到一个相邻的落脚点,并且最后回到这个落脚点。
  教主在任意时刻,都会有“上升”、“下降”两种状态的其中一种。
  当教主从第i个落脚点,走到第j个落脚点的时候(i和j相邻)
  j的海拔高于i的海拔:如果教主处于上升状态,教主需要耗费两段高度差的绝对值的体力;否则耗费高度差平方的体力。
  j的海拔低于i的海拔:如果教主处于下降状态,教主需要耗费两段高度差的绝对值的体力;否则耗费高度差平方的体力。  
  当然,教主可以在到达一个落脚点的时候,选择切换自己的状态(上升→下降,下降→上升),每次切换需要耗费M点的体力。在起点的时候,教主可以自行选择状态并且不算切换状态,也就是说刚开始教主可以选择任意状态并且不耗费体力。
  教主希望花费最少的体力,让嫦娥成为自己的女朋友。

Input
  输入的第一行为两个正整数N与M,即落脚点的个数与切换状态所消耗的体力。
  接下来一行包含空格隔开的N个正整数,表示了每个落脚点的高度,题目保证了相邻落脚点高度不相同。

Output
  输出仅包含一个正整数,即教主走一圈所需消耗的最小体力值。
  注意:C++选手建议使用cout输出long long类型整数。

Sample Input

6 7
4 2 6 2 5 6

Sample Output

27

Hint
【样例说明】
  从第3个落脚点开始以下降状态向前走,并在第4个落脚点时切换为上升状态。
  这样共耗费4 +(7)+3+1+22+22+4=27点体力。
【数据规模】
  对于10%的数据,N ≤ 10;
  对于30%的数据,N ≤ 100,a[i] ≤ 1000;
  对于50%的数据,N ≤ 1000,a[i] ≤ 100000;
  对于100%的数据,N ≤ 10000,a[i] ≤ 1000000,M ≤ 1000000000;

简要思路:这题是一个比较容易出(chao)错(shi)的环形DP,比较原始的方法就是枚举开头的点,不过这样 n n n方的复杂度会爆。
纪中国庆10.5做题小结_第1张图片
由上面这幅图可以发现,根据平移的原理,从哪个点开始,顺时针还是逆时针都不会影响最后的答案。
所以,我们把输入的山在尾部再加上头部第一座山即可。

没那么快完呐,如果教主从 1 − 2 1-2 12 n − 1 n-1 n1的状态一样,那不就多算了吗?
我们用 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]来表示教主在当前状态下消耗的总体力值, i i i表示第 i i i座山, j = 0 j = 0 j=0表示下降状态, j = 1 j = 1 j=1表示上升状态, k = 0 k = 0 k=0表示没有状态的转换, k = 1 k = 1 k=1表示有状态的转换。

如此看来,似乎要用多种 d p dp dp,不过对初值进行限制可以固定 d p dp dp的方向,详见代码吧。

#include 
#include 
#include 
#include 
#define N 10005
#define ll long long
#define inf 10000000000000000LL
//#define sqr(x) ((x)*(x))
//#define abs(x) ((x>0?x:(-x)))不知为何用了会出错
using namespace std;
int n;
ll m;
ll a[N] , f[N][2][2];//1 上升 //  0 下降 
template < typename T >
inline void read( T & res ) {
	res = 0;
	T pd = 1;
	char aa = getchar();
	while ( aa < '0' || aa > '9' ) {
		if ( aa == '-' ) {
			pd = -pd;
		}
		aa = getchar();
	}
	while ( aa >= '0' && aa <= '9' ) {
		res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
		aa = getchar();
	}
	res *= pd;
	return;
}
inline ll sqr( ll x ) {
	return x * x;
}
inline void dp() {
	f[1][0][1] = f[1][1][1] = inf;
	for ( int i = 2 ; i <= n + 1 ; ++i ) {
		for ( int j = 0 ; j <= 1 ; ++j ) {
			if ( ( a[i] < a[i - 1] ) ^ j ) {
				f[i][j][0] = f[i - 1][j][0] + abs( a[i] - a[i - 1] );
				f[i][j][1] = min( f[i - 1][j][1] , min( f[i - 1][j^1][0] , f[i - 1][j^1][1] ) + m ) + abs( a[i] - a[i - 1] );
			} else {
				f[i][j][0] = f[i - 1][j][0] + sqr( a[i] - a[i - 1] );
				f[i][j][1] = min( f[i - 1][j][1] , min( f[i - 1][j^1][0] , f[i - 1][j^1][1] ) + m ) + sqr( a[i] - a[i - 1] );
			}
		}
	}
	return;
}
int main () {
	read(n);
	read(m);
	for ( int i = 1 ; i <= n ; ++i ) {
		read(a[i]);
	}
	a[n + 1] = a[1];
	memset( f , 0 , sizeof(f) );
	dp();
	ll ans = min( min( f[n + 1][0][0] , f[n + 1][0][1] ) , min( f[n + 1][1][0] , f[n + 1][1][1] ) );
	//前面的dp是为了处理一般情况,后两个是为了处理多算转换状态的代价的情况
	memset( f , 0 , sizeof(f) );
	f[1][0][0] = inf;//dp只能由上升方向开始
	dp();
	ans = min( ans , f[n + 1][1][1] - m );
	memset( f , 0 , sizeof(f) );
	f[1][1][0] = inf;//dp只能由下降方向开始
	dp();
	ans = min( ans , f[n + 1][0][1] - m );
	printf("%lld",ans);
	return 0;
}

T3:保镖排队

Description
【问题背景】
  教主LHX作为知名人物,时刻会有恐怖分子威胁他的生命。于是教主雇佣了一些保镖来保障他的人生安全。
【题目描述】
  教主一共雇佣了N个保镖,编号为1~N。每个保镖虽然身手敏捷武功高强,但是他在其余N-1个保镖里,都会有一个“上司”,他会对他的上司言听计从。但一号保镖例外,他武功盖世,不惧怕其余任何保镖,所以他没有上司。
  教主LHX会对这N个保镖进行定期视察。每次视察的时候,首先会让所有保镖排队。
  对于每个保镖,在他心目中会对他的所有下属的武功实力排个队。
  现在教主要求排出来的队伍满足:①互为上司-下属的两个保镖,上司在前,下属在后 ②对于一个保镖的所有下属,武功实力较强的在前,较弱的在后。
  教主想知道,总的排队方法数除以10007的余数是多少。

Input
  输入的第一行为一个正整数T,表示了数据组数。
  对于每组数据:
  第一行为一个正整数N。
  接下来N行,每行描述一个保镖。
  第i+1行,会有一个整数K,代表第i个保镖的下属个数,接下来K个数,代表第i个保镖的下属按照武功实力从高到低的编号。

Output
  输出包括C行,每行对于每组数据输出方案数mod 10007后的结果。

Sample Input

2
5
2 2 3
2 4 5
0
0
0
7
2 2 3
2 4 5
2 6 7
0
0
0
0

Sample Output

3
10

Hint
【样例说明】
  对于第1组数据,有以下3种排列是合法的:
  1 2 4 3 5
  1 2 3 4 5
  1 2 4 5 3
  同时满足了1在2与3之前且2在3之前,2在4与5之前且4在5之前
【数据规模】
  对于20%的数据,有N ≤ 9;
  对于40%的数据,有对于所有K,有K ≤ 2;
  对于60%的数据,有N ≤ 100;
  对于100%的数据,有T ≤ 10,N ≤ 1000,K ≤ N。

简要思路:这题就是多叉树转二叉树以及一些排列组合的小推导。
根据样例一中每个保镖的从属关系,不难得出如下图的一棵树:

对这棵树,我们要找到从父到子,子节点从左到右的所有构造方法,不过这样处理有些麻烦,而且其它数据还不一定是二叉树,更麻烦。不过,后面我想到了一本通里面介绍的多叉转二叉法,即"父连长兄,长兄为父",上图可转化如下:
纪中国庆10.5做题小结_第2张图片
此时处理非常方便,只要元素放进去的顺序满足新二叉树的父子顺序即可,同时二叉树也非常好处理,最后只要考虑 x x x个元素与 y y y个元素混合,满足每组元素各自排列的顺序的方案数即可。两组的排列总数为 ( x + y ) ! (x+y)! (x+y)!,满足那 x x x个元素排列顺序的可能为 1 x ! 1 \over x! x!1,满足那 y y y个元素排列顺序的可能为 1 y ! 1 \over y! y!1。所以总方案数为 ( x + y ) ! x ! ∗ y ! (x + y)! \over x!*y! x!y!(x+y)!,即 C x + y x 或 y C_{x+y}^{x或y} Cx+yxy,预处理一下即可。

#include 
#include 
#include 
#define mod 10007
#define N 1005
using namespace std;
int c[N][N];
int head[N];
int siz[N] , tim[N];
int bcnt;
struct node{
	int next;
	int to;
}str[N];
int t , n;
template < typename T >
inline void read( T & res ) {
	res = 0;
	T pd = 1;
	char aa = getchar();
	while ( aa < '0' || aa > '9' ) {
		if ( aa == '-' ) {
			pd = -pd;
		}
		aa = getchar();
	}
	while ( aa >= '0' && aa <= '9' ) {
		res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
		aa = getchar();
	}
	res *= pd;
	return;
}
inline void pre() {
	c[0][0] = 0;
	c[1][0] = 1;
	c[1][1] = 1;
	for ( int i = 2 ; i <= 1000 ; ++i ) {
		c[i][0] = 1;
		for ( int j = 1 ; j <= i ; ++j ) {
			c[i][j] = ( c[i - 1][j - 1] + c[i - 1][j] ) % mod;
		}
	}
	return;
}
inline void insert( int from , int to ) {
	str[++bcnt].next = head[from];
	head[from] = bcnt;
	str[bcnt].to = to;
	return;
}
inline void dfs( int cur ) {
	siz[cur] = 1;
	tim[cur] = 1;
	for ( int i = head[cur] ; i ; i = str[i].next ) {
		dfs( str[i].to );
		siz[cur] += siz[str[i].to];
		tim[cur] = ( tim[cur] * tim[str[i].to] ) % mod;
		if ( i != head[cur] ) {
			tim[cur] = ( tim[cur] * c[siz[cur] - 1][siz[str[i].to]] ) % mod;
		}
	}
	return;
}
int main () {
	pre();
	read(t);
	while ( t-- ) {
		bcnt = 0;
		read(n);
		memset( head , 0 , sizeof(head) );
		int num , x , pre;
		for ( int i = 1 ; i <= n ; ++i ) {
			read(num);
			pre = i;
			while ( num-- ) {
				read(x);
				insert( pre , x );
				pre = x;
			}
		}
		dfs( 1 );
		printf("%d\n",tim[1]);
	}
	return 0;
}

T4:教主的别墅

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

简要思路:这题主要是用贪心思想,首先用 s u m sum sum数组来存贮状态(表示1的个数减去0的个数),用 z e ze ze统计 s u m sum sum为零出现的次数,若最后 s u m [ n ] sum[n] sum[n]为0,不难发现,若 z e ze ze大于等于 m m m,则0,1个数差最大值最小为0(这个显然);若 z e ze ze小于 m m m,可证0,1个数差最大值最小为1(引理1)。当上述两个情况都不满足时,可证0,1个数差最大值最小为 ⌈ ∣ s u m [ n ] ∣ m ⌉ \lceil {|sum[n]| \over{m}} \rceil msum[n](引理2)。

证明:先证引理1,一个严格划分的最小0,1差为0的字符串最两边端必定是不同的数,如下图,纪中国庆10.5做题小结_第3张图片
该图可在红线处再次化分,不合题意;
纪中国庆10.5做题小结_第4张图片
这幅图才合格。
此时,我们让两端的数各成一家,中间的子串0,1差为0,两边的为一,不矛盾。
只要再处理中间的即可。
处理过程中中间的函数图像会下降一个单位,可能出现如下情况:
纪中国庆10.5做题小结_第5张图片
按红线划分即可。
再证引理2,先考虑 s u m [ n ] = m sum[n]=m sum[n]=m的情况:我们不妨设1的个数比0多,不难得到下面这幅函数图像:
纪中国庆10.5做题小结_第6张图片
红线圈出的区域就是0,1差为0的子串,直接忽略。
蓝线圈出的区域就是藏在中间的0,1差为0的子串。
此时不难发现,去除两边0,1差为0的子串后,得出的子串必定是被一共 s u m [ n ] sum[n] sum[n]个1包围,中间为0,1差为0的子串的串(绕口啊 )。我们让这 s u m [ n ] sum[n] sum[n]个1各自成家,如果跟0,1差为0的子串接壤就顺带收了它。
s u m [ n ] = m sum[n]=m sum[n]=m的情况已证,当 s u m [ n ] > m sum[n]>m sum[n]>m时,从左到右优先合并两个0,1差最小的子串即可;当 s u m [ n ] < m sum[n]sum[n]<m时,先拆开多余的1和0,1差为0的子串,再拆分子串,根据引理1,0,1差都不会大于1,引理2得证。
定理证明后,接下来就好办了,得出最大0,1差,字典序最小方案就是从前到后能分就分,字典序最大方案就是给数组倒个序做相同处理,能分割的判定条件是 a b s ( s u m [ n ] − s u m [ i ] ) < = t o t ∗ ( m − c n t − 1 ) abs( sum[n] - sum[i] ) <= tot * ( m - cnt - 1 ) abs(sum[n]sum[i])<=tot(mcnt1)其中tot为最大0,1差,cnt为已划分的块数,至于原因,自己体会吧。
PS:上述函数图像实际上为折线,但博主太懒只想画曲线qwq。

#include 
#include 
#include 
#include 
#include 
#define N 5000005
#define M 100005
using namespace std;
int num[N] , sum[N] , ans[M];
char ss[N];
int n , m;
template < typename T >
inline void read( T & res ) {
	res = 0;
	T pd = 1;
	char aa = getchar();
	while ( aa < '0' || aa > '9' ) {
		if ( aa == '-' ) {
			pd = -pd;
		}
		aa = getchar();
	}
	while ( aa >= '0' && aa <= '9' ) {
		res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
		aa = getchar();
	}
	res *= pd;
	return;
}
inline void print() {
	bool f = 1;
	for ( int i = 1 ; i <= m ; ++i ) {
		if ( f ) {
			f = 0;
		} else {
			printf(" ");
		}
		printf("%d",ans[i]);
	}
	printf("\n");
}
inline void bprint() {
	bool f = 1;
	for ( int i = m ; i >= 1 ; --i ) {
		if ( f ) {
			f = 0;
		} else {
			printf(" ");
		}
		printf("%d",ans[i]);
	}
}
inline void work() {
	memset( sum , 0 , sizeof(sum) );
	int ze = 0;
	int pre = 0;
	for ( int i = 1 ; i <= n ; ++i ) {
		if ( num[i] ) {
			pre++;
		} else {
			pre--;
		}
		sum[i] = pre;
		if ( pre == 0 ) {
			ze++;
		}
		//cout << pre << endl;
	}
	int tot = 0;
	if ( !sum[n] ) {
		if ( ze >= m ) {
			tot = 0;
		} else {
			tot = 1;
		}
	} else {
		tot = abs( abs( sum[n] ) - 1 ) / m + 1;
	}
	//cout << tot << endl;
	int cnt = 0;
	int cur = 0;
	for ( int i = 1 ; i <= n ; ++i ) {
		if ( cnt >= m - 1 ) {
			ans[++cnt] = n + 1 - i;
			break;
		}
		if ( abs( sum[n] - sum[i] ) <= tot * ( m - cnt - 1 ) ) {
			ans[++cnt] = i - cur;
			cur = i;
		}
		//cout << cur << endl;
	}
	return;
}
int main () {
	read(n);
	read(m);
	scanf("%s",ss);
	for ( int i = 1 ; i <= n ; ++i ) {
		num[i] = ss[i - 1] - '0';
	}
	work();
	print();
	reverse( num + 1 , num + 1 + n );
	work();
	bprint();
	return 0;
}

你可能感兴趣的:(国庆集训)