Atcoder Regular Contest 128 E - K Different Values(思维题)
隔两个月补题解的时候甚至连题面都忘记了。。。
先考虑有解的充要条件,令 \(S=\sum a_i\),考虑 \(c=\lfloor\dfrac{S}{k}\rfloor,g=(S-1)\bmod k+1\),换句话说,我们把整个序列分成若干个长度为 \(k\) 的块,那么 \(c\) 是块的个数,\(g\) 的最后一个块的长。那么显然解存在的必要条件是:
- \(\forall i,a_i\le c\)。
- \(\sum\limits_{i=1}^n[a_i=c]\le g\)。
在下文中简称为“条件”。读者自证不难。
我们希望能够通过构造过程来说明以上条件同时也是充分条件。考虑通过以下策略构造出字典序最小的解:
- 如果此时 \(\sum\limits_{i=1}^n[a_i=c]=g\),说明接下来 \(g\) 个位置必须都要填上此时 \(a_i=c\) 的元素,我们就找到没有过去 \(k-1\) 个元素中出现过的且 \(a_i=c\) 的最小元素填在当前位置。
- 否则我们直接找出最小的没有在过去 \(k-1\) 个元素中出现过的元素填在当前位置。
每次操作完以后令长度减一,计算出新的 \(c\) 和 \(g\) 以后归纳处理剩余子问题即可。显然每次操作后条件仍然成立。
考虑证明该做法的正确性:
- 如果某一次填数对应了第一种情况,并且此时不存在 \(a_i=c\) 且没有在过去 \(k-1\) 个元素中出现过的元素,那么考虑倒推到 \(k-1\) 个时刻之前,此时 \(g\) 变小 \(1\) 但是 \(\sum[a_i=c]\) 不变,说明早在 \(k-1\) 个时刻之前就已经出现违背条件的情况,矛盾!
- 如果某一次填数对应了第二种情况,并且此时不存在 \(a_i>0\) 且没有在过去 \(k-1\) 个元素中出现过的元素,那么 \(\sum [a_i\ne 0]\le k-1\),而后面的空位数必须 \(\le k-1\),故此时 \(c=1\),并且由于条件成立,故 \(\forall a_i>0,a_i=1\),故 \(\sum\limits_{i=1}^n[a_i=c]=g\),故此次填数对应的实际上是第一种情况,矛盾!
时间复杂度 \(O(Sn)\)。
const int MAXN=500;
const int MAXM=2e5;
int n,m,k,p[MAXM+5],q[MAXM+5],a[MAXN+5],res[MAXM+5];
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d%d",&n,&k);for(int i=1;i<=n;i++)scanf("%d",&a[i]),m+=a[i];
for(int i=0;i<m;i++)p[i]=(m-i+k-1)/k,q[i]=(m-i-1)%k+1;
int c=0;
for(int i=1;i<=n;i++){
if(a[i]>p[0])return puts("-1"),0;
if(a[i]==p[0])++c;
}if(c>q[0])return puts("-1"),0;
for(int i=0;i<m;i++){
static bool vis[MAXN+5];memset(vis,0,sizeof(vis));
for(int j=max(i-k+1,0);j<i;j++)vis[res[j]]=1;
c=0;for(int j=1;j<=n;j++)c+=(a[j]==p[i]);res[i]=-1;
if(c==q[i]){for(int j=1;j<=n;j++)if(a[j]==p[i]&&!vis[j]){res[i]=j;break;}}
else{for(int j=1;j<=n;j++)if(a[j]&&!vis[j]){res[i]=j;break;}}
assert(res[i]!=-1);a[res[i]]--;
}for(int i=0;i<m;i++)printf("%d%c",res[i]," \n"[i==m-1]);
return 0;
}