纪中暑假集训 2020.08.03【NOIP提高组】模拟 T3:【NOIP2015模拟11.5】Divide

【NOIP2015模拟11.5】Divide

Description

纪中暑假集训 2020.08.03【NOIP提高组】模拟 T3:【NOIP2015模拟11.5】Divide_第1张图片

Input

在这里插入图片描述

Output

纪中暑假集训 2020.08.03【NOIP提高组】模拟 T3:【NOIP2015模拟11.5】Divide_第2张图片

Sample Input

输入1:
4 100
4 5 2 25
输入2:
12 1
1 1 1 1 1 1 1 1 1 1 1 1
输入3:
27 360
269 154 94 221 171 154 50 210 258 358
121 159 8 47 290 125 291 293 338 248
295 160 268 227 99 4 273

Sample Output

输出1:
2
输出2:
220
输出3:
114

Data Constraint

纪中暑假集训 2020.08.03【NOIP提高组】模拟 T3:【NOIP2015模拟11.5】Divide_第3张图片

反思&题解

比赛思路: 暴力……(居然爆0了,听说数据long long也会爆,打gcd才行)
正解思路: 先拿一个数组存一下p的因数,因为质因子 2 ∗ 3 ∗ 5 ∗ 7 ∗ 11 ∗ 13 ∗ 17 ∗ 19 2*3*5*7*11*13*17*19 235711131719是最接近且大于p的结果,所以p的因子最多有 2 8 = 256 2^8=256 28=256个,三重循环复杂度 O ( 25 6 3 ) O(256^3) O(2563)显然不会爆,
之后我们再拿一个桶记录每个a[i]与p的gcd出现的次数,之后枚举p的每个因子,
设分别为i,j,k,有5种情况
1. i = j = k i=j=k i=j=k时,就是在p的因子i里面选择3个,即

a n s + = C p 的 因 子 i 出 现 的 个 数 3 ans+=C^3_{p的因子i出现的个数} ans+=Cpi3

2. i = j ≠ k i=j≠k i=j=k时,就是在p的因子i里面选择2个,在k里面选择1个,即

a n s + = C p 的 因 子 i 出 现 的 个 数 2 ∗ C p 的 因 子 k 出 现 的 个 数 1 ans+=C^2_{p的因子i出现的个数}*C^1_{p的因子k出现的个数} ans+=Cpi2Cpk1

3. i ≠ j = k i≠j=k i=j=k时,参考情况2
4. i = k ≠ j i=k≠j i=k=j时,也参考情况2
5. i ≠ j ≠ k i≠j≠k i=j=k时,就是在p的因子i,j,k种各选择一个,即

a n s + = C p 的 因 子 i 出 现 的 个 数 1 ∗ C p 的 因 子 j 出 现 的 个 数 1 ∗ C p 的 因 子 k 出 现 的 个 数 1 ans+=C^1_{p的因子i出现的个数}*C^1_{p的因子j出现的个数}*C^1_{p的因子k出现的个数} ans+=Cpi1Cpj1Cpk1

最后因为这题时不带模数的,所以费马小定理求组合数就用不了,我们就可以将式子化简来求,化简我在这就不写了,留给大家思考的空间,实在想不到就看code吧
反思: 思维很重要,转化题目有时候时AC的关键

CODE

#include
using namespace std;
long long n,p,yz[1000005],tong[1000005],ans;
long long gcd(long long x,long long y)
{
	if (x%y==0) return y;
	else return gcd(y,x%y);
}
int main()
{
	freopen("divide.in","r",stdin);
	freopen("divide.out","w",stdout);
	scanf("%lld%lld",&n,&p);
	long long i;
	for (i=1;i<=p;i++)
		if (p%i==0) yz[++yz[0]]=i;
	for (i=1;i<=n;i++)
	{
		long long x;
		scanf("%lld",&x);
		tong[gcd(x,p)]++;		
	}
	long long j,k;
	for (i=1;i<=yz[0];i++)
	{
		for (j=i;j<=yz[0];j++)
		{
			for (k=j;k<=yz[0];k++)
			{
				if (yz[i]*yz[j]*yz[k]%p==0)
				{
					if (i==j && j==k) ans+=tong[yz[i]]*(tong[yz[j]]-1)*(tong[yz[k]]-2)/6;
					else if (i==j) ans+=tong[yz[i]]*(tong[yz[j]]-1)*tong[yz[k]]/2;
					else if (j==k) ans+=tong[yz[j]]*(tong[yz[k]]-1)*tong[yz[i]]/2;
					else if (i==k) ans+=tong[yz[i]]*(tong[yz[k]]-1)*tong[yz[j]]/2;
					else ans+=tong[yz[i]]*tong[yz[j]]*tong[yz[k]];
				}
			}
		}
	}
	printf("%lld\n",ans);
	return 0;
}

你可能感兴趣的:(题解,反思)