题目链接
翻译
让你按顺序对连续的点进行染色(总共有 (m) 个连续块需要染色)
你可以指定这个连续块的区间,但是长度必须是 (li) (但不能超过边界)
然后后面的染色会覆盖前面的染色,且每个连续块的染色(要染的颜色)都不一样。
要求 (m) 次染色过后,所有 (m) 种颜色都至少还存在一个位置。且每个点(1-n)都至少被染色过一次。
题解
两种情况:
第 (1) 种,所有的 (l) 的值加起来还不到 (n),那么无论怎么染色都有点没染到。无解
第 (2) 种,考虑最紧凑的染色方法,即第 (i) 次染色的位置 (p[i] = p[i-1] + 1) 且 (p[1]=1) 也即 (p[i]=i),这样只会留一个上次染色的颜色在位置 (i-1),这是最紧凑的情况了。
对于这种紧凑的染色方法,如果某个位置还有 (i+l[i]-1>n) 这就说明第 (i) 种颜色无论怎么染,都会让前面的某些颜色消失。所以无解。
我们考虑这两种情况,其实就对应了宽松和紧凑两种染色方法,宽松也即所有的染色连续块都不相交。紧凑也即连续两个染色块非常紧凑地连在一起。
那我们直接用第二种方法不就行了?
但是有一个问题,就是只用第二种方法的话,可能会出现在后面某些位置没有染上色的情况,所以得宽松和紧凑两种方法结合使用。
开始的时候后面所有的 (l[i]) 的值加上当前位置比 (n) 大(宽松摆就会超过 (n) 了),那没关系,我们就按照紧凑的方法摆,也即 (p[i]=i)
一旦某个时刻后面所有的 (l[i]) 的值加上当前位置比 (n) 小了,或者等于了。这说明,再这样紧凑着摆的话,就不够 (n) 个格子都染上色了。
则我们从最后一段 ([n-l[m]+1,n]) 开始宽松的染色(倒数第二段是([n-l[m]-l[m-1]+1,n-l[m]]),直到和上一个 (p[i-1]=i-1) 的紧凑染色覆盖的区域有交集为止。
(真正输出的时候,对于第 (i) 次需要开始紧凑染色的位置,就直接输出(n-∑_{k=i}^ml_k)+1就好,对应了这种逆着宽松染色的过程)
代码1
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 1e5;
int n, m;
int l[N + 10];
int main(){
scanf("%d%d",&n,&m);
LL rest = 0;
for (int i = 1;i <= m; i++){
scanf("%d",&l[i]);
rest += l[i];
}
if (rest < n){
puts("-1");
return 0;
}
for (int i = 1;i <= m; i++){
if (i + l[i] - 1 > n){
puts("-1");
return 0;
}
}
for (int i = 1;i <= m; i++){
if (i + rest - 1 > n){
printf("%d",i);
}else{
printf("%d",n - rest + 1);
}
if (i == m){
puts("");
}else{
putchar(' ');
}
rest -= l[i];
}
return 0;
}