zoukankan      html  css  js  c++  java
  • 【luogu P5435】基于值域预处理的快速 GCD(数学)

    基于值域预处理的快速 GCD

    题目链接:luogu P5435

    题目大意

    要你 O(1) 求两个数的 gcd,其中值域是 1~1e6。

    思路

    考虑把要求的数分成若干个部分乘起来的结果。
    然后这些部分都是小于 (sqrt{_{值域}}) 的(除了大质数),然后我们可以通过下面的方法分出三个。
    (不过是大质数也无所谓,它是质数就也可以搞)

    那接着考虑怎么分出来,这里先给出方法,就你欧拉筛的时候把枚举到的最小的质数乘在那个分好的三个中最小的那个,然后重新排序。
    然后这里给证明:(证明为什么不会超过 (sqrt{_{值域}})
    我们这里让 (n) 是要分解的数,(p) 是最小的质数
    首先显然 (dfrac{n}{p}) 的分解中最小的 (a) 是不大于 (sqrt[3]{frac{n}{p}}) 的(规定)。

    然后 (a*pleqslantsqrt[3]{frac{n}{p}}*p)
    (p>sqrt[4]{n}) 的时候,很明显分解出的三个是它的三个质因子,一定是对的。
    否则化一下式子:
    (a*pleqslantsqrt[3]{frac{n}{p}}*p)
    (a*pleqslantsqrt[3]{frac{n}{sqrt[4]{n}}}*sqrt[4]{n}=sqrt[3]{n}*frac{1}{sqrt[3]{sqrt[4]{n}}}*(sqrt[3]{sqrt[4]{n}})^3=sqrt[3]{n}*(sqrt[3]{sqrt[4]{n}})^2=sqrt[3]{nsqrt{n}}=sqrt[3]{sqrt{n}^2sqrt{n}}=sqrt{n})

    然后就证出来了。

    然后就好啦。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define mo 998244353
    
    using namespace std;
    
    int n, a[5001], b[5001];
    int pr[1000001][4], prime[500001];
    int gcd[1001][1001];
    ll ans, di;
    
    int GCD(int x, int y) {
    	int re = 1, noww;
    	for (int i = 0; i <= 2; i++) {
    		if (pr[x][i] <= 1000) {
    			noww = gcd[pr[x][i]][y % pr[x][i]];
    		}
    		else {//大于的部分只可能是值域,直接要么倍数是本身,要么是 1
    			if (y % pr[x][i] == 0) noww = pr[x][i];
    				else noww = 1;
    		}
    		y /= noww;
    		re *= noww;
    	}
    	return re;
    }
    
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    	for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
    	
    	pr[1][0] = pr[1][1] = pr[1][2] = 1;//拆解
    	for (int i = 2; i <= 1000000; i++) {
    		if (!pr[i][0]) {
    			pr[i][0] = pr[i][1] = 1; pr[i][2] = i;
    			prime[++prime[0]] = i;
    		}
    		for (int j = 1; j <= prime[0] && i * prime[j] <= 1000000; j++) {
    			memcpy(pr[i * prime[j]], pr[i], sizeof(pr[i * prime[j]]));
    			pr[i * prime[j]][0] *= prime[j];
    			sort(pr[i * prime[j]], pr[i * prime[j]] + 3);
    		}
    	}
    	
    	for (int i = 1; i <= 1000; i++) gcd[i][0] = gcd[0][i] = i;
    	for (int i = 1; i <= 1000; i++)//预处理根号值域内两两的 gcd
    		for (int j = 1; j <= 1000; j++) {
    			if (i > j) gcd[i][j] = gcd[j][i % j];
    				else gcd[i][j] = gcd[i][j % i];
    		}
    	
    	for (int i = 1; i <= n; i++) {
    		di = 1; ans = 0;
    		for (int j = 1; j <= n; j++) {
    			di = di * i % mo;
    			ans = (ans + 1ll * di * GCD(a[i], b[j]) % mo) % mo;
    		}
    		printf("%lld
    ", ans);
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    【转自己的落谷博客】联通块的dfs
    STL中map的简单使用&常数优化
    转自自己的关于落谷计数器【p1239】的题解
    计算机网络三种模型
    docker的常用的一些配置
    容器整体性理解
    如何在类中定义装饰器?
    如何实现属性可修改的函数装饰器?
    如何定义带参数装饰器?
    如何为被装饰的函数保存元数据?
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_P5435.html
Copyright © 2011-2022 走看看