Solutions usaco A chn
第一眼感觉是是维护一个 hash 集合来存最终已经占用的值对每个数,如果冲突就不断加上 \(k\) 就可以了,直到变成没出现过的值再插入。
随机数据下这看起来可能挺快,但在最坏情况下第 \(i\) 次插入可能需要 \(\Theta(i)\) 次重试,于是总重试次数是 \(\Theta(n^2)\),这样我们就拿到了前两个子任务。
我们考虑优化,我们需要一个有保证的界。
令 \(d=|k|\)。关键不变量是对任意数 \(a\) 加 \(k\) 永远不会改变它对 \(d\) 的余数。因此冲突只可能发生在同一个余数类内部。
我们按 \(d=|k|\) 把所有数分桶就行了。
分桶大概是这样的。
ll d = llabs(k);
int s;
if(k>0) s = 1;
else s = -1;
vector<vector<ll>> f(d);
for(int i=0;i<n;i++){
ll r=a_i%d;
ll b=(a_i-r)/d;
ll x=(ll)s*b;
f[r].push_back(x);
}
然后我们开始对每个桶开始做处理。
我们首先要排序因为我们得从小的开始处理如果我们首先处理大的数就会让小的数被迫更大。
排序完之后,我们就开始扫桶维护这个处理到的目前的最大的数我们假设它为 \(p\)。
这里有一个很直觉的贪心也就是如果我们扫到的当前值 \(x\) 如果他大于我们的 \(p\) 的话我们就不需要做处理否则我们就只需要把它抬高到 \(p+1\) 这个位置即可。
对于每个桶大概是这样的。
sort(v.begin(),v.end());
ll p = (ll)-4e18;
for(ll x:v){
if(x <= p){
ans+=(p+1-x);
x=p+1;
}
p = x;
}
总的时间复杂度也就是 \(O(n\log n)\)。

浙公网安备 33010602011771号