zoukankan      html  css  js  c++  java
  • 类欧几里得算法

    这个板子是真(TM)毒。心脑血管疾病患者慎入


    传送门

    总之先来康柿子,给定非负整数(n,a,b,c),求下面三个柿子(这里的(g,h)顺序好像和题目颠倒了......问题不大):

    [f(a,b,c,n) = sumlimits_{i = 0}^{n} leftlfloor frac{ai + b}{c} ight floor ]

    [g(a,b,c,n) = sumlimits_{i = 0}^{n} i leftlfloor frac{ai + b}{c} ight floor ]

    [h(a,b,c,n) = sumlimits_{i = 0}^{n} leftlfloor frac{ai + b}{c} ight floor ^ 2 ]

    并输出对(998244353)取模的结果,保证(0 le n,a,b,c le 10^9, c ot= 0)


    (f(a,b,c,n)):

    由于(a)非负,所以(leftlfloor frac{ai + b}{c} ight floor)对于(i)是递增的,为了方便,下面令(d(i) = leftlfloor frac{ai + b}{c} ight floor)

    换句话说,(d(i))的最大值就是(d(n)),我们令(m = d(n))

    那么就有

    [d(i) = sumlimits_{j = 1}^{m} left[ j le d(i) ight] ]

    (j = j-1)(为什么呢?等会就知道了。)

    [d(i) = sumlimits_{j = 0}^{m - 1} left[ j+1 le d(i) ight] ]

    然后把取整符号去掉

    [d(i) = sumlimits_{j = 0} ^ {m-1} left[ j+1 le frac{ai + b}{c} ight] ]

    我们还能进一步化简中括号里的柿子

    [j + 1 le frac{ai + b}{c} Leftrightarrow cj + c - b le ai ]

    这里直接两边除以(a)?然而并不能。因为除以(a)后我们肯定会再把取整符号填上去,但(frac{cj + c - b}{a} le i)并不能说明(leftlfloor frac{cj + c - b}{a} ight floor le i)

    所以我们还是要把两边减一再变

    [cj + c - b - 1 < ai Leftrightarrow frac{cj + c - b - 1}{a} < i Leftrightarrow leftlfloor frac{cj + c - b - 1}{a} ight floor < i ]

    再带回(d(i)),同时令(alpha = alpha(j) = leftlfloor frac{cj + c - b - 1}{a} ight floor)

    [d(i) = sumlimits_{j = 0}^{m - 1} left[ i > alpha ight] ]

    回到原式

    [egin{aligned} f(a,b,c,n) &= sumlimits_{i = 0}^{n} d(i) \ &= sumlimits_{i = 0}^{n} sumlimits_{j = 0}^{m - 1} left[ i > alpha ight] end{aligned} ]

    交换求和顺序再来颓柿子

    [egin{aligned} f(a,b,c,n) &= sumlimits_{j = 0} ^ {m - 1} sumlimits_{i = 0} ^ {n} [i > alpha] \ &= sumlimits_{j = 0}^{m - 1} { (n+1) - (alpha + 1) } \ &= sumlimits_{j = 0} ^ {m - 1} { n - alpha} \ &= nm - sumlimits_{j = 0}^{m - 1} alpha end{aligned} ]

    后面的(sumlimits_{j = 0}^{m - 1} alpha)也就是(f(c, c -b - 1, a, m - 1)),于是(上面让(j)(0)开始就是为了这一步)

    [f(a,b,c,n) = nm - f(c,c-b-1,a,m-1) ]

    没了?并没有......

    发现如果(c - b - 1 < 0)的话这个柿子就炸了......

    考虑让(b)(c)取模,顺便也把(a)带走((a = 0)也是边界)。

    [egin{aligned}d(i) &= leftlfloor frac{ai + b}{c} ight floor \&= leftlfloor frac{(leftlfloor frac{a}{c} ight floor c + a mod c)i + leftlfloor frac{b}{c} ight floor c + b mod c}{c} ight floorend{aligned} ]

    注意到(leftlfloor frac{a}{c} ight floor c i)(leftlfloor frac{b}{c} ight floor c)都是整数,且有约数(c),直接提出来

    [d(i) = leftlfloor frac{a}{c} ight floor i + leftlfloor frac{b}{c} ight floor + leftlfloor frac{(a mod c) i + b mod c}{c} ight floor ]

    于是

    [f(a,b,c,n) = frac{n(n+1)}{2}leftlfloor frac{a}{c} ight floor + (n+1)leftlfloor frac{b}{c} ight floor + f(a mod c, b mod c, c, n) ]

    这样就可以递归算了,边界就是(a = 0)

    [f(a,b,c,n) = egin{cases}nm - f(c, c-b-1, a, m - 1) quad (a < c and b < c) \frac{n(n+1)}{2}leftlfloor frac{a}{c} ight floor + (n+1)leftlfloor frac{b}{c} ight floor + f(a mod c, b mod c, c, n) quad (a ge c or b ge c) \(n+1) leftlfloor frac{b}{c} ight floor quad (a = 0)end{cases} ]

    时间复杂度:

    观察(a)位置上和(c)位置上的数,第一次是((a,c))(不放设(a < c)),第(2)次会变为((c,a)),第三次((c mod a, a))然后再交换位置,再取模。这不就是(gcd)吗?于是复杂度就是(gcd)的复杂度(*2)


    (g(a,b,c,n))

    类似上面的(f),我们也照这个样子推导(g)。(一些定义和上面类似)。

    (a ge c or b ge c)

    [egin{aligned}g(a,b,c,n) &= sumlimits_{i = 0}^{n} i cdot d(i)end{aligned} ]

    (d(i))展开

    [egin{aligned}g(a,b,c,n)&= sumlimits_{i = 0} ^ n i cdot (leftlfloor frac{a}{c} ight floor i + leftlfloor frac{b}{c} ight floor + leftlfloor frac{(a mod c)i + b mod c}{c} ight floor) \&= sumlimits_{i = 0} ^ n leftlfloor frac{a}{c} ight floor i^2 + leftlfloor frac{b}{c} ight floor i + i cdot leftlfloor frac{(a mod c)i + b mod c}{c} ight floor \&= frac{1}{6}n(n+1)(2n+1)leftlfloor frac{a}{c} ight floor + frac{1}{2}n(n+1)leftlfloor frac{b}{c} ight floor + g(a mod c, b mod c, c, n)end{aligned} ]

    (a < c and b < c)

    [egin{aligned}g(a,b,c,n)&= sumlimits_{i = 0}^{n} i cdot d(i) \&= sumlimits_{i = 0} ^ n i sumlimits_{j = 0}^{m - 1}[i > alpha] \&= sumlimits_{j = 0} ^ {m-1} sumlimits_{i = 0}^{n} i cdot [i > alpha]end{aligned} ]

    第二个(sum)就是对(alpha+1...n)这些整数求和,直接套公式

    [egin{aligned}g(a,b,c,n)&= sumlimits_{j = 0}^{m - 1} frac{1}{2}(n-alpha)(n+alpha+1) \&= frac{1}{2} sumlimits_{j = 0}^{m - 1} n^2 + nalpha + n - nalpha - alpha^2 - alpha \&= frac{1}{2}left[ mn(n+1) - h(c, c-b-1, a, m-1) - f(c, c-b-1, a,m-1) ight]end{aligned} ]

    这里用到了(f)(h)

    边界就是(a = 0)时,(g = frac{1}{2}n(n+1)leftlfloor frac{b}{c} ight floor)

    发现(g)也是一个类似于(f)的递归过程,可以和(f)放到一起算。


    (h(a,b,c,n))

    感觉平方项很难通过枚举值算贡献的方法化简......

    怎么办呢?

    考虑到(1+2+...+n = frac{1}{2}n(n+1)),那么(n^2 = 2left(sumlimits_{k = 0}^{n}k ight) - n)

    带到(d(i))里再带回原柿就是

    [egin{aligned}h(a,b,c,n)&= sumlimits_{i = 0}^{n} d(i)^2 \&= sumlimits_{i = 0}^{n} 2 left( sumlimits_{k = 0} ^ {d(i)} k ight)-d(i) \&= left( 2 sumlimits_{i = 0} ^ n sumlimits_{k = 0} ^ {d(i)}k ight) - f(a,b,c,n)end{aligned} ]

    考虑怎么求(sumlimits_{i = 0} ^ n sumlimits_{k = 0} ^ {d(i)}k),同样的,让(j)代替(k),并从(0)跑到(m-1),然后交换求和顺序

    [sumlimits_{j = 0} ^ {m - 1} sumlimits_{i = 0} ^ {n} [j +1 le d(i)] cdot (j+1) ]

    中括号里我们已经化简过了,再把(j+1)提到外面

    [egin{aligned}sumlimits_{j =0}^{m-1} (j+1) sumlimits_{i = 0}^{n} [i>alpha]&= sumlimits_{j = 0}^{m-1}(j+1)(n-alpha) \&= sumlimits_{j = 0}^{m - 1} n(j+1) - j cdot alpha - alpha \&= frac{1}{2} m(m+1)n - g(c, c-b-1, a, m-1) - f(c, c-b-1, a, m-1)end{aligned} ]

    所以(h(a,b,c,n) = m(m+1)n - 2g(c,c-b-1,a,m-1) - 2f(c,c-b-1,a,m-1) - f(a,b,c,n))

    取模的话暴力三元完平展开就好了

    [h(a,b,c,n) = h(a mod c, b mod c, c, n) + 2 leftlfloor frac{b}{c} ight floor f(a mod c, b mod c, c, n) + 2leftlfloor frac{a}{c} ight floor g(a mod c, b mod c, c, n) + frac{1}{6}n(n+1)(2n+1) leftlfloor frac{a}{c} ight floor ^ 2 + (n+1) leftlfloor frac{b}{c} ight floor^2 + n(n+1) leftlfloor frac{a}{c} ight floor leftlfloor frac{b}{c} ight floor ]

    边界就是(a = 0)(h(a,b,c,n) = leftlfloor frac{b}{c} ight floor ^ 2(n+1))(h)也可以和(f,g)放到一起算。

    总复杂度是(gcd)的复杂度。


    (Code):

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    inline ll read() {
    	char ch; ll x = 0; int f = 1; while (!isdigit(ch = getchar())) f = ch=='-' ? -f : f;
    	while (isdigit(ch)){ x = x*10 + ch - '0'; ch = getchar(); } return x * f;
    }
    
    const int P = 998244353, i2 = 499122177, i6 = 166374059;
    void update(ll &x, ll y) {
    	x += y; if (x >= P) x -= P;
    }
    inline ll s1(ll n) {
    	n %= P; return n % P * (n+1) % P * i2 % P;
    }
    inline ll s2(ll n) {
    	n %= P; return n % P * (n+1) % P * (2ll*n%P + 1) % P * i6 % P;
    }
    inline ll sqr(ll x) {
    	return 1ll * x * x % P;
    }
    
    #define chk(x) assert(0<=(x) && (x)<P)
    
    void euclid(ll a, ll b, ll c, ll n, ll &f, ll &g, ll &h) {
    	ll ac = a/c, bc = b/c, m = 1ll * (1ll*a*n + b) / c;
    	if (a == 0) {
    		f = 1ll * bc * (n + 1) % P, g = 1ll * bc * s1(n) % P;
    		h = 1ll * sqr(bc) * (n + 1) % P;
    		return;
    	}
    	ll f1, g1, h1;
    	if (a >= c || b >= c) {
    		euclid(a % c, b % c, c, n, f1, g1, h1);
    		f = g = h = 0;
    		update(f, 1ll * s1(n) * ac % P), update(f, 1ll * bc * (n+1) % P), update(f, f1);
    		update(g, 1ll * s2(n) * ac % P), update(g, 1ll * s1(n) * bc % P), update(g, g1);
    		update(h, 1ll * s2(n) * sqr(ac) % P), update(h, 1ll * (n+1) * sqr(bc) % P);
    		update(h, h1), update(h, 2ll * ac * g1 % P);
    		update(h, 1ll * n * (n+1) % P * ac % P * bc % P);
    		update(h, 2ll * bc * f1 % P);
    		return;
    	}
    	euclid(c, c-b-1, a, m-1, f1, g1, h1);
    	f = g = h = 0;
    	update(f, 1ll * n * m % P), update(f, P - 1ll * f1 % P);
    	update(g, 1ll * n % P * (n+1) % P * m % P);
    	update(g, P - 1ll * f1 % P), update(g, P - 1ll * h1 % P), g = g * i2 % P;
    	update(h, 1ll * m * (m+1) % P * n % P), update(h, P - 2ll * g1 % P);
    	update(h, P - 2ll * f1 % P), update(h, P - f);
    }
    
    int main(){
    	for (int T = read(); T; T--) {
    		ll n = read(), a = read(), b = read(), c = read(), f, g, h;
    		euclid(a, b, c, n, f, g, h);
    		printf("%lld %lld %lld
    ", f, h, g);
    	}
    	return 0;
    }
    

    一般形式的话......估计这辈子都学不动了(QwQ)

  • 相关阅读:
    专家视角 | 小荷的 Oracle Database 18c 新特性快速一瞥
    java.lang.ClassCastException: com.xx.User cannot be cast to com.xx.User
    上传单个文件
    极速体验:Oracle 18c 下载和Scalable Sequence新特性
    开工大吉:Oracle 18c已经发布及新特性介绍
    CentOS7编译安装NodeJS
    .NET 同步与异步之锁(ReaderWriterLockSlim)(八)
    .NET 同步与异步之锁(ReaderWriterLockSlim)(八)
    .NET 同步与异步之锁(ReaderWriterLockSlim)(八)
    .NET 同步与异步之锁(ReaderWriterLockSlim)(八)
  • 原文地址:https://www.cnblogs.com/wxq1229/p/12215748.html
Copyright © 2011-2022 走看看