题解:CF2254D Silhouette
chenqishuo · · 题解
题意简述
给定长度为
要求构造一个字典序最小的正整数数组 -1。
思路
设原数组
对于某个数值
其中
因此,我们先将给定的
显然,最小的
- 若
m = 1 ,即所有b_i = 0 ,则所有a_i 可以相等,取最小值1 即可。 -
否则,
c_1 = 0 。对于第一组(b=0 ),其a 值记为v_1 。由于c_2 是所有小于v_2 的元素之和,而小于v_2 的只有v_1 ,所以c_2 = v_1 \cdot sz_1 \quad \Rightarrow \quad v_1 = \frac{c_2}{sz_1} 必须整除且
v_1 > 0 。一般地,对于
i = 2, 3, \dots, m-1 ,有c_{i+1} - c_i = v_i \cdot sz_i 所以
v_i = \frac{c_{i+1} - c_i}{sz_i} 必须整除且满足
v_i > v_{i-1} 。对于最后一组
c_m ,它应该等于所有比它小的元素之和,即c_m = \sum_{p=1}^{m-1} v_p \cdot sz_p 若该等式不成立则无解。同时,
v_m 只需大于v_{m-1} ,可取v_{m-1}+1 (但c_m 的等式已经包含了所有较小的元素,v_m 本身不影响影子值,所以可取最小合法值)。
若上述条件均满足,则每个位置
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int T, n, m, sz[N];
LL c[N], val[N];
pair<LL, int> b[N];
LL ans[N];
int main()
{
scanf("%d", &T);
while(T --)
{
bool flag = true;
LL sum = 0;
scanf("%d", &n);
for(int i = 1;i <= n; ++ i)
{
scanf("%lld", &b[i].first);
b[i].second = i;
}
sort(b + 1, b + n + 1);
m = 0;
for(int i = 1;i <= n; )
{
int j = i;
while(j <= n && b[j].first == b[i].first) j ++;
m ++;
c[m] = b[i].first;
sz[m] = j - i;
i = j;
}
if(c[1] != 0) flag = false;
if(flag && m == 1)
val[1] = 1;
else if(flag)
{
if((c[2] - c[1]) % sz[1] != 0) flag = false;
else
{
val[1] = (c[2] - c[1]) / sz[1];
if (val[1] <= 0) flag = false;
}
for(int i = 2; i < m && flag; ++ i)
{
if((c[i + 1] - c[i]) % sz[i] != 0) flag = false;
else
{
val[i] = (c[i + 1] - c[i]) / sz[i];
if (val[i] <= val[i - 1]) flag = false;
}
}
if(flag)
{
val[m] = val[m - 1] + 1;
for (int i = 1;i < m; ++ i)
sum += val[i] * sz[i];
if (c[m] != sum) flag = false;
}
}
if(!flag)
{
printf("-1\n");
continue;
}
int j = 1;
for (int i = 1;i <= n; ++ i)
{
while(j < m && b[i].first > c[j]) j ++;
ans[b[i].second] = val[j];
}
for (int i = 1; i <= n; ++ i)
printf("%lld%c", ans[i], i == n ? '\n' : ' ');
}
return 0;
}
在这里给个提醒,就是尽量使用 scanf 和 print,我赛时第一发用了关闭同步流被卡了。
原题通过记录