[jzoj]3526. 【NOIP2013模拟11.7A组】不等式(solve)(类欧几里得)

来源:互联网 发布:飞跃淘宝店铺信息软件 编辑:程序博客网 时间:2024/05/22 12:11

https://jzoj.net/senior/#contest/show/2061/2

Problem

已知L,R,S,M,求满足L<=(sx) mod m<=R的最小正整数x. 如果无解输出1.

Data constraint

30%的数据中保证有解并且答案小于等于10^6;
另外20%的数据中保证L=R;
100%的数据中T<=100,M、S、L、R<=10^9。

Solution

【拓展gcd解二元一次方程】


30%的数据我们可以直接暴力.

对于20%的L=R,其实可以把式子转化为(Sx) mod m=L,间接转化为Sxym=L,即为一个二元一次方程的求解,用拓展gcdO(log2n)的时间内求解即可得到50分.


【分类讨论】

由原式

L<=(Sx) mod m<=R

分类讨论:

①当式子满足有关于不等式

L<=Sx<=R
的解x为正整数时,此时可以直接求出满足条件的最小x的值,即为答案.

②当不满足①时,并满足有关于不等式

L<=(Sx) mod m<=R
的解x为正整数时,我们可以通过类欧几里得的思想求解关于x的不等式.


【类欧几里得】

即类似欧几里得递归求解的一种方法.


【一个重要性质】

观察可得知,[L..R]区间不存在S的倍数的数.


【标准化】

我们使要进行类欧几里得的四个变量m,s,l,r标准化,保证有

0<l<=r<m0<s<m
【第一个不等式范围解释】
任何一个模上m的数的取值范围只在0m1,若l>=m,不等式无解,所以保证了l<m,也正因此,我们可以使得r不超过m1,否则不等式没有意义.

保证l>0是因为如果有l=0则一定有最小整数解x=0,注意这里的x=0只是在已经缩小到一个范围内的界限的解,最终答案一定不可能为0.

【第二个不等式范围解释】
因为(ab) mod c=(a mod c)(b mod c).

我们用s mod m保证了s<m.

且如果有s=0,则x=0一定为最优解,边界已经产生,无需继续递归求解.理由同上


【转化模型】

此时,我们再来转化一下式子,由②式我们可以转化成

L<=Sxmy<=R

进一步转化为以y为主元的式子:
R<=mySx<=L

因为在此不等式内,不含有S的倍数,所以不等式可以同时加上KS再取模S而不影响答案.

这一步是关键,理解也并不难.

你可以把模操作看成减去一个数,因为l..r区间内没有s的倍数,r..l 区间同样没有,所以负数取模等于加上了同一个数,不等式两端同时加上一个数,不等式仍然成立.

故有

R+KS<=MySx+KS<=L+KS

我们不需要K,使得边界为非负数,只需取模后再加上S即可:
((R mod S)+S) mod S<=My mod S<=((L mod S)+S) mod S

易证取模后可以保证边界为非负数.


【一个问题】

如何证明每次递归一定能使范围缩小.

其实我们只需证明每次的s是减小的,因为l,r都是不超过s的最大正数,所以每次也是减小的.

而证明s减小就很显然了,等同于欧几里得gcd(a,b)=gcd(b,a mod b)


【旧事重提】

现在我们终于可以用类似欧几里得的思想去递归求解.

先设函数sxgcd(l,r,m,s)表示②式的解.

通过上述可以得出

sxgcd(l,r,m,s)=sxgcd((r mod s+s)mod s,(l mod s+s) mod s,s,m)


【判断边界及回代】

注意判断边界的可行性.再已经产生解时直接返回.

并且在每次求出

L<=sxmy<=R
的解y时,要把x的最小整数解再代回去,而不是代y的值回去.

这里是关键!


Code

var        t,m,s,l,r,x:int64;function sxgcd(m,s,l,r:int64):int64;begin        if r>=m then r:=m-1;        s:=s mod m;        if (l>r) or (l>=m) or ((s=0) and (l>0)) then exit(-1);        if (l=0) then exit(0);                       //保证标准化        if ((l-1) div s+1)*s<=r then exit((l-1) div s+1); //可行解        sxgcd:=sxgcd(s,m,(-r mod s+s) mod s,(-l mod s+s) mod s); //类欧几里得        if sxgcd=-1 then exit(-1);          x:=(m*sxgcd+l-1) div s+1;   //回代可能的最小整数解x        if s*x-m*sxgcd>=1 then exit(x) else exit(-1); //判负end;begin        assign(input,'solve.in'); reset(input);        assign(output,'solve.out'); rewrite(output);        readln(t);        while t>0 do        begin                dec(t);                readln(m,s,l,r);                writeln(sxgcd(m,s,l,r));        end;        close(input); close(output);end.
阅读全文
0 0