zoukankan      html  css  js  c++  java
  • 基础数论函数练习题

    Problem

    (n) 个数,每个数为 (a_i),有 (Q) 次询问,每次询问 ([l,r]),求 (LCM_{i=l}^r a_i)。总共 (T) 组数据。

    数据范围:(1leq n, Q, Tleq 300)(1leq a_ileq 2^{60})

    Solution

    首先暴力分解质因数然后搞的最多只有 (40) 分。

    考虑另一种暴力:将 (LCM) 表示成若干个数的乘积以避免数字太大。记 (lcm_{l,r}=LCM_{i=l}^r),我们用 (r-l+1) 个数 (b_i) 的乘积表示 (lcm_{l,r})。考虑纳入 (a_{r+1}),注意到 (lcm_{l,r+1}=dfrac{lcm_{l,r} imes a_{r+1}}{(lcm_{l,r},a_{r+1})}),增加的因子为 (dfrac{a_{r+1}}{(lcm_{l,r},a_{r+1})}),将其记为 (b_{r+1}),利用 ((a,b)=(amod b,b)) 求解。通过这样我们可以用 (mathcal O(Tn^3log V)) 的复杂度求解,其中 (V)(a_i) 的最大值。

    使用分治优化:考虑求解(lcm_{i,j}),其中 (lleq ileq mid,mid<jleq r)。首先求出来 (lcm_{l,mid}=prod_{i=l}^{mid}b_i^{[L]})(lcm_{mid+1, r}=prod_{i=mid+1}^rb_{i}^{[R]})。注意这里求解 (b^{[L]}) 从右往左,(b^{[R]}) 从左往右,这样满足 (lcm_{i,mid}=prod_{j=i}^{mid}b_j^{[L]})(lcm_{mid+1,i}=prod_{j=mid+1}^ib_j^{[R]})

    接下来将 (lcm_{i,mid}) 逐个因子地合并到右边。从 (mid)(l),每次枚举一个因子 (b_i^{[L]}),让其从左往右继续使用 ((A,prod_i B_i)), 对 (b_j^{[R]}) 消公约数。此时可以求出 (Ans_{i,j}=prod _iA_iprod_i B_i)。复杂度 (mathcal O(Tn^2log V))

    使用技巧将复杂度降成 (mathcal O(Tn(n+log nlog V)))。发现复杂度瓶颈在求 (Ans_{i,j}) 这里,是由于求 GCD 的缘故。采用下列方法将复杂度由 一次 (mathcal O(nlog V)) 降成 (mathcal O(log V))。注意到这里我们实际上要做的是这样的过程

    t = b[i];
    for j = mid+1 -> r
    	d = (t, b[j]);
    	t = t / d;
    	b[j] = b[j] / d;
    

    这里 (t) 约分不超过 (log b_i) 次,说明只有 (leqlog b_i)(d e 1)。令 (c_j=prod_{mid<kleq j}b_k),则 (prod_{mid<kleq j}d_k=(b_i, c_k))(d_k) 是每次约分的值。记 (G=(b_i,c_r)),当第一次遇到 (b_j mod G e 0) 时,必有 ((b_i,b_j) e (b_i,b_{j+1})),利用 (G'=(G, c_j)) 得到约分的 (d_j=dfrac G{G'})。如此利用下去总 GCD 复杂度仍然为 (mathcal O(log G)leqmathcal O(log V))。加上分治的 (log n),可以通过本题。

    Code

    #include <bits/stdc++.h>
    typedef long long LL;
    const int N = 305, P = 1e9 + 7;
    int T, n, Q, ans[N][N];
    LL a[N], b[N], c[N];
    LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; }
    LL mul(LL a, LL b, LL mod) {
    	LL t = a * b - (LL)((long double)a / mod * b + 0.5) * mod; t %= mod;
    	return t < 0 ? t + mod : t;
    }
    void solve(int l, int r) {
    	if (l == r) { ans[l][r] = a[l] % P; return; }
    	int mid = l + r >> 1;
    	solve(l, mid), solve(mid + 1, r);
    	for (int i = mid; i >= l; i--) {
    		LL t = 1;
    		for (int j = mid; j > i; j--) t = mul(t, b[j], a[i]);
    		b[i] = a[i] / gcd(a[i], t);
    	}
    	for (int i = mid + 1; i <= r; i++) {
    		LL t = 1;
    		for (int j = mid + 1; j < i; j++) t = mul(t, b[j], a[i]);
    		b[i] = a[i] / gcd(a[i], t);
    	}
    	int s = 1;
    	for (int i = mid; i >= l; i--) {
    		c[mid] = 1;
    		for (int j = mid + 1; j <= r; j++) c[j] = mul(c[j - 1], b[j], b[i]);
    		LL G = gcd(c[r], b[i]);
    		for (int j = r; j > mid; j--)
    			if (c[j - 1] % G) {
    				LL d = gcd(G, c[j - 1]);
    				b[j] /= G/d, G = d;
    			}
    		s = b[i] % P * s % P;
    		int s2 = s;
    		for (int j = mid + 1; j <= r; j++)
    			ans[i][j] = s2 = b[j] % P * s2 % P;
    	}
    }
    int main() {
    	scanf("%d", &T);
    	while (T--) {
    		scanf("%d%d", &n, &Q);
    		for (int i = 1; i <= n; i++)
    			scanf("%lld", &a[i]);
    		solve(1, n);
    		for (int i = 1; i <= Q; i++) {
    			int l, r; scanf("%d%d", &l, &r);
    			printf("%d
    ", ans[l][r]);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    (十)安装YAML
    (十四)日志
    Android自动化测试------monkeyrunner(六)
    Android自动化测试------monkey日志管理(五)
    如何学好一门编程语言
    linux vi hjkl由来
    .net 微服务CICD 好文章
    APPCMD 精心整理 有点赞的吗
    APPCMD 命令合集
    APPCMD 使用手册
  • 原文地址:https://www.cnblogs.com/ac-evil/p/14432614.html
Copyright © 2011-2022 走看看