Codeforces 1119G - Get Ready for the Battle(构造)

一道还行的构造。

首先答案的下界肯定是 \(\lceil\dfrac{\sum hp_i}{n}\rceil\),考虑能否达到之。

一个挺奇怪的地方在于怪物的个数和可以将士兵分成的堆数相等,这启发我们从这里加以思考。

为了方便起见,如果 \(\sum hp_i\) 不是 \(n\) 的倍数,我们就增加 \(hp_n\) 使得 \(\sum hp_i\) 变为最小的 \(n\) 的倍数。显然为了达到这个下界,我们每次必须恰好使总和减少 \(n\),不能有一点浪费。我们尝试让每次打的是一段怪物的连续的区间加上一段左右两端的零头,换句话说,任何时候打死的怪物是一段长度为 \(len\) 的前缀,并且第 \(len+2\) 个怪物到第 \(m\) 个怪物都毫发无损。思考一下达成这个目标需要满足什么条件。考虑一段前缀 \([1,i]\),记 \(sum_i=\sum\limits_{j=1}^ihp_j\),那么在第 \(\lfloor\dfrac{sum_i}{n}\rfloor\) 轮后,前 \(i\) 个怪物只剩下 \(sum_i\bmod n\) 的血量了,为了能够凑出足够的兵打死这个怪物,需要满足存在一个士兵的集合满足这个集合中的兵力总和恰好等于 \(sum_i\bmod n\)。而这样的限制恰好有 \(m\) 个,我们也恰好可以将士兵分成 \(m\) 堆。芜湖,我们终于搞懂为什么怪物个数和士兵堆数相等了,于是问题也就迎刃而解——将 \(sum_i\bmod n\) 从小到大排序,然后取它们的差分数组作为各堆士兵的人数,这样可以保证所有 \(sum_i\) 都可以被表示出来,然后按顺序依次打过去即可。

时间复杂度线性。

const int MAXN=1e6;
int n,m,a[MAXN+5],s[MAXN+5],r[MAXN+5],c[MAXN+5],cur=1;
int main(){
#ifdef LOCAL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%d%d",&n,&m);for(int i=1;i<=m;i++)scanf("%d",&a[i]),s[i]=s[i-1]+a[i];
	int lim=(s[m]+n-1)/n;a[m]+=lim*n-s[m];s[m]=lim*n;
	for(int i=1;i<=m;i++)r[i]=s[i]%n;r[m]=n;sort(r+1,r+m+1);
	printf("%d\n",lim);
	for(int i=1;i<=m;i++)printf("%d%c",c[i]=r[i]-r[i-1]," \n"[i==m]);
	for(int i=1;i<=m;i++){
		while(a[i]){
			a[i]-=c[cur];printf("%d%c",i," \n"[cur==m]);
			++cur;if(cur==m+1)cur=1;
		}
	}
	return 0;
}
posted @ 2022-12-22 15:46  tzc_wk  阅读(27)  评论(0)    收藏  举报