P1022 · OFFICIAL SOLUTION

P1022 千仓平丰歉 官方题解

Gioush OJ · P1022 千仓平丰歉

千仓平丰歉

【题目简述】

nn 个数 cic_i。每次可以选择一个位置,把它增加 aabb。求任意次操作后,所有数极差的最小值。

【数据点 1∼41\sim41∼4】

【题目描述】

nn 很小时,可以枚举最终所有数落入的区间,并逐个判断每个 cic_i 能否通过若干次加 aa、加 bb 落入其中。

【Hint】

加法操作不会改变 gcd(a,b)\gcd(a,b) 的余数。这个不变量比原数值更重要。

【解法】

d=gcd(a,b)d=\gcd(a,b)。每个 cic_i 在任意操作后模 dd 的余数不变。反过来,由裴蜀定理,足够大的同余位置都可以由 cic_i 经过若干次操作到达。因此这一档已经说明:只需考虑余数在模环上的相对位置。

【正解】

【题目描述】

ri=cimoddr_i=c_i\bmod d。现在要求在长度为 dd 的环上找到最短的一段连续弧,覆盖全部 rir_i

【Hint】

与其直接找被覆盖部分,不如找哪一段空隙不经过。

【解法】

将余数排序为 r1r2rnr_1\le r_2\le\cdots\le r_n。所有未出现余数形成的空隙中,最大的一个长度为

Max(r1+drn, maxi=2n(riri1)).\operatorname{Max}\left(r_1+d-r_n,\ \max_{i=2}^{n}(r_i-r_{i-1})\right).

选择这段最大空隙不经过,剩下的短弧恰好覆盖所有余数,所以答案为

dMax(r1+drn, maxi=2n(riri1)).d-\operatorname{Max}\left(r_1+d-r_n,\ \max_{i=2}^{n}(r_i-r_{i-1})\right).

这也给出了构造:把每个数抬高到该短弧中与其同余的位置即可。

【复杂度分析】

排序一次,时间复杂度为 O(nlogn)\mathcal{O}(n\log n),空间复杂度为 O(n)\mathcal{O}(n)

【参考代码】

#include <cstdio>#include <algorithm>#define MAXN 100010using namespace std;int T,n,a,b,c[MAXN];int Gcd(int x,int y){    return y?Gcd(y,x%y):x;}void Solve(){    scanf("%d%d%d",&n,&a,&b);    int d=Gcd(a,b);    for(int i=1;i<=n;i++) scanf("%d",&c[i]),c[i]%=d;    sort(c+1,c+n+1);    int Max=c[1]+d-c[n];    for(int i=2;i<=n;i++) Max=max(Max,c[i]-c[i-1]);    printf("%d\n",d-Max);}int main(){    scanf("%d",&T);    while(T-->0) Solve();    return 0;}