ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2021牛客多校第二场J

2021-09-22 19:02:23  阅读:173  来源: 互联网

标签:prime ch int ll 多校 牛客 2021 ans 欧拉


题意:给n个数,求所有从这n个数中选k个构成一组的gcd的乘积

思路:因为每个数都可以分解为质数的乘积,所以我们考虑枚举gcd就是枚举所有质数以及他的倍数的gcd;假设现在有一个质数p,要取k个数使他的gcd为p,则只可能是p的倍数(2p,3p……),假设n个数中共有m个p的倍数,则贡献就是C_{m}^{k};但是对于p²等p的幂次就相当于少算了(就是应该多乘几个p),所以把p的幂次也进行一次计算(实在不明白就找一个例子测试一下就行,反正我是想了很久还感觉迷迷糊糊的)

看不懂就借鉴这几篇文章吧:2021牛客多校 Product of GCDs(拓展欧拉定理+欧拉函数)_m0_51841071的博客-CSDN博客

队内训练2 牛客多校第二场补题J 拓展欧拉定理_ojbkoxy的博客-CSDN博客

总结一下:

1.枚举gcd,即枚举质数p以及它的所有幂次(当然要小于个数的最大值)

2.对于p或者他的幂次,计算这个数即他的倍数在n个数中出现的次数m

3.计算q=C_{m}^{k},最后每次枚举的结果就是p^q

注意:

 1.k很大,用扩展欧拉降幂 (常数很大,你忍一下)

2.要用欧拉降幂,你又得知道欧拉函数

3.在求欧拉函数时,因为枚举常数会超时,所以你还需要枚举素数,所以你要求出所有素数

前置知识:

1.欧拉函数(f(x)代表小于等于x并与x互质的个数):

对于p^n(p为质数)来说:f(p^n)=(p-1)p^(n-1);

证明:对于p^n,与他不互质的数只有0,p,2*p……p^n-p,共有p^n/p个,所以与他互质的数就有p^n-p^n/p=(p-1)p^(n-1);

然后是一般情况下,欧拉函数的计算流程。其中用到了前两个性质。把n质因数分解,然后把各个质因子带入最终公式,计算欧拉函数值。(节选自百度百科)

证明过程如图:

数论:欧拉函数的计算与性质(Mathematica)

就用这个就可以写出暴力求n的欧拉函数的函数  phi(n)(之前不知道为什么脑子没有绕过弯,死活想不明白):

ll phi(ll x)
{
    __int128 ans=x;//超范围
    for (int i=0;primes[i]<=x/primes[i];i++)//正常这里是枚举常数,但是因为超时,所以这里是枚举质数。。。离谱
//先除再乘,防止超范围
    {
        if (x%primes[i]==0)//prime[i]是x的质因数
        {
            ans=ans/primes[i]*(primes[i]-1);//套公式
            
            while(x%primes[i]==0) x/=primes[i];//去除所有含有的这个质因子
        }
    }
    
    if (x>1) ans=ans/x*(x-1);//x原本是素数的情况,没有质因子,特殊考虑
    return ans;
}

2.欧拉降幂:

欧拉定理、拓展欧拉定理及其应用(欧拉降幂法) - Reqaw - 博客园详细见此

 3.最后快速幂就行

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 40010,M=10000010;
typedef long long ll;
int a[N];//真的怪,把这个数组改为a然后就超时,超时就算了,再改回来原来不超时的代码超时了!然后复制原来一模一样的代码交了,又能过了,真的离谱
//好了,我现在写个注释都会超时了
ll c[N][31];//计算组合数
int own[2*N];//计算每个数的次数
int prime[M],cnt;//根号P内的质数就行,因为我们是要求P的欧拉函数嘛
bool st[M];//是否为质数
int n, k;
ll P,euler;
//还要快读。。。这时间卡的
//大整数乘法:对于c=a?b的运算,由于a和b范围是10^14,直接乘会爆long long,
//因此可以用__int128做中间处理。

inline int read()
{
	int x = 0, f = 1; char ch = getchar();
	while (ch < '0' || ch>'9')
	{
		if (ch == '-')f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9')
	{
		x = x * 10 + ch - '0'; ch = getchar();
	}
	return x * f;
}

inline ll lread()
{
	ll x = 0, f = 1; char ch = getchar();
	while (ch < '0' || ch>'9')
	{
		if (ch == '-')f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9')
	{
		x = x * 10 + ch - '0'; ch = getchar();
	}
	return x * f;
}

void Isprime()//质数筛
{
	for (int i = 2; i < M; i++)
	{
		if (!st[i])
		{
			prime[cnt++] = i;
			for (int j = i * 2; j < M; j += i)
			{
				st[j] = true;
			}
		}
	}
}

ll phi(ll x)//欧拉函数
{
	__int128 ans = x;
	for (int i = 0; x / prime[i] >= prime[i]; i++)
	{
		if (x % prime[i] == 0)
		{
			ans = ans / prime[i] * (prime[i] - 1);
			while (x % prime[i] == 0)
			{
				x /= prime[i];
			}
		}
	}
	if (x > 1)
	{
		ans = ans / x * (x - 1);
	}
	return ans;
}

ll quick_mi(int p, ll m)//快速幂
{
	__int128 ans = 1, base = p;
	while (m)
	{
		if (m & 1)
		{
			ans = (ans * base) % P;
		}
		base = (base * base) % P;
		m >>= 1;
	}
	return ans%P;
}

int main()
{
	Isprime();
	int t;
	t = read();
	while (t--)
	{
		memset(own, 0, sizeof(own));//记得清零!!!!!!
		int Max = 0;
		n = read(), k = read(), P = lread();
		for (int i = 0; i < n; i++)
		{
			a[i]= read();
			own[a[i]]++;
			Max = max(a[i], Max);
		}

		euler = phi(P);
		c[0][0] = 1;
		for (int i = 1; i <= n; i++)//求组合数
		{
			c[i][0] = 1;
			for (int j = 1; j <= min(k, i); j++)
			{
				c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
				if (c[i][j] >= euler)
				{
					c[i][j] = c[i][j] % euler + euler;//扩展定理
				}
			}
		}
		ll ans = 1;
		for (int i = 0; prime[i] <= Max; i++)
		{
			for (int j = prime[i]; j <= Max; j *= prime[i])
			{
				int m = 0;
				for (int q = j; q <= Max; q += j)
				{
					m += own[q];
				}
				if (m >= k)
				{
					ans = __int128(ans)* quick_mi(prime[i], c[m][k])%P;
				}
				else
				{
					break;//因为没退出wa了。。。。。 
				}
			}
		}
		printf("%lld\n",ans%P);
	}
	return 0;
}

欧拉公式延伸(这些没用到,但是了解一下挺好的,放最后吧):

一个数所有的质因数的和=f(n)*n/2

(后面那句话是解释来着。。。)妙啊!!!震惊

 wow,想明白了好神奇,反正就是这样

标签:prime,ch,int,ll,多校,牛客,2021,ans,欧拉
来源: https://blog.csdn.net/Gone_Summer/article/details/120392222

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有