题目链接
翻译
让你按顺序对连续的点进行染色(总共有 \(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;
}