FZU 2031 计数问题

题意:

f(x) = x^x , (x的x次方)

令 g(x,y) = f(x) + f(y)

求 g(x,y) = 0 (mod p) 的数对(x,y)数量 ,其中 (x,y)为整数对,且1<=x<=y<p , p为质数,且p<1000 000,测试数据组数T = 100


解题思路:

f(x) + f(y) = 0 (%p) , 由于p为质数,根据费马小定理,x^(p-1) =1(%p),  x^(x) * x^(p-1-x) = 1(%p) ,故 f(x)  (%p) 不为0。 (1<=x<p)

对于f(x) + f(y) = 0 (%p) 可知 x!=y

记 num[x]为 次方%p为x的数的个数

答案为  ∑num[i]*num[n-i]   (1<=i<p)


思路1:求出1..p-1的所有f(x)值,每次用快速幂计算,时间复杂度 O(T*n*logn) ,超时

思路2:根据线性筛素数的方法变形,计算出f(x)值:

如下:

f(1) = 1^1

f(2) = 2^2  f(4)  = (2^2)*(2^2) * (2^4)

f(3) = 3^3  f(6) = (3^3)*(3^3) * (2^6)                f(9) = (3^6)*(3^3) * (3^9) 

f(4) =                  f(8) = (4^4)*(4^4) * (2^8) f(12) = (4^8)*(4^4) * (3^12)

f(5) = 5^5  f(10) = (5^5)*(5^5) * (2^10)            f(15) = (5^10)*(5^5) * (3^15)                 f(25) = (5^15)*(5^10) * (5^25)

f(6) =                 f(12) = (6^6)*(6^6) * (2^12)            f(18) = (6^12)*(6^6) * (3^18)                 f(30) = (6^18)*(6^12) * (5^30)

……

因此只需计算出质数的x次方,就可以计算出所有f(x)值,n以内素数个数大约为n/10,因此计算出素数次方总共需要 n/10 * logn,求出所有f(x)是线性的,因此总时间复杂度为O(T*n/10*logn),超时

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
typedef long long ll;
using namespace std;

int num[1010000];
int pri[1010000],P_num;
int pp[1010000],rpp[1010000],stamp[130];
bool use[1010000];

int powM(int a,int b,int m){
	int ans=1;
	while (b){
		if (b&1) ans = (ll)ans*a%m;
		a = (ll)a*a%m;
		b>>=1;	
	}
	return ans;
}
void getP(int P){
	memset(use,false,sizeof(use));
	P_num=0;
	pp[1]=1;
	pri[0]=0;
	for (int i=2;i<P;i++)
	{
		if (!use[i]) { 
			pri[++P_num]=i;
			pp[i] = powM(i,i,P);
		}
		
		int tmp=1,len=2;
		stamp[1]=pp[i];
		stamp[2]=(ll)pp[i]*pp[i]%P;
		for (int j=1;j<=P_num&&i*pri[j]<P;j++){
			use[i*pri[j]]=true;
			if (len<120)
			{
				len+=2;
				stamp[len] = (ll)stamp[len-2] * pp[i] *pp[i]%P;
			}
			tmp = (ll)tmp*stamp[pri[j]-pri[j-1]]%P;
			if (i==pri[j])
				rpp[j] = tmp;
			else
				rpp[j] = (ll)rpp[j]*pp[pri[j]]%P;
			pp[i*pri[j]] = (ll) tmp * rpp[j]%P;
		}
	}
}
int main(){
	int T;
	scanf("%d",&T);
	while (T-->0){
		int n;
		scanf("%d",&n);
		memset(num,0,sizeof(num));
		getP(n);
		ll ans=0;
		for (int i=1;i<n;i++){
			num[pp[i]]++;
		//	cout<<i<<":"<<pp[i]<<endl;
			ans+=num[n-pp[i]];	
		}

		printf("%I64d\n",ans);
	}
	return 0;
}

思路3:考虑到整个计算是在模p剩余类群当中,求出关于p的一个原根r,则该群可由原根表示{r^1,r^2,r^3,.....,r^(p-1) },因此只需预处理出r的1到p-1次方,就可以表示出1..n-1的所有数,

例如:

p = 5,取原根 2

{1,2,3,4} 可以表示为{2^4, 2^1, 2^3, 2^2 }

然后根据欧拉降幂公式

1^1 = (2^4)^1 = 2^4 = 2^(4%4)

2^2 = (2^1)^2 = 2^2 = 2^(2%4)

3^3 = (2^3)^3 = 2^9 = 2^(9%4)

4^4 = (2^2)^4 = 2^8 = 2^(8%4)

时间复杂度O(n),通过

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
typedef long long ll;
using namespace std;

int num[1010000];
int pp[1010000];


// 求素数 
int P_num,pri[1010000],pre[1010000];
bool use[1010000];
void getPrime(){
	memset(use,0,sizeof(use));
	memset(pre,0,sizeof(pre));
	P_num = 0;
	for (int i=2;i<1010000;i++){
		if (!use[i]) pri[++P_num]=i, pre[i]=i;
		for (int j=1;j<=P_num&&i*pri[j]<1010000;j++) {
			use[i*pri[j]]=true;	
			if (i%pri[j]!=0)
				pre[i*pri[j]]=pri[j];
			else pre[i*pri[j]] = pre[i];
		}
	}
}

int powM(int a,int b,int M){
	int ans=1;
	while (b){
		if (b&1) ans= (ll)ans*a%M;
		a = (ll)a*a%M;
		b>>=1;	
	}
	return ans;
}


//求原根
int fac[30];
int getPrimitiveRoot(int P){
	int cnt=0;
	int x = P-1;
	while (x>1){
		fac[++cnt] = pre[x];
		x /= pre[x];	
	}
	
	int ans = 0;
	for (int i=2;i<P;i++){
		bool isProot=true;
		for (int j=1;j<=cnt;j++)
			if (powM(i,(P-1)/fac[j],P)==1){
				isProot = false;
				break;
			}
		if (isProot) {
			ans = i;
			break;	
		}
	}
	return ans;
}


int f2[1010000],powt[1010000];
void work(int P,int proot){
	int t=1;
	powt[0]=1;
	for (int i=1;i<P;i++){
		t = t*proot%P;
		powt[i] = t;
		f2[t] = i;	
	}
	for (int i=1;i<P;i++)
		pp[i] = powt[(ll)f2[i]*i%(P-1)];
}

int main(){
	getPrime();

	int T;
	scanf("%d",&T);
	while (T-->0){
		int n;
		scanf("%d",&n);
		memset(num,0,sizeof(num));
		work(n,getPrimitiveRoot(n));
		ll ans=0;
		for (int i=1;i<n;i++){
			num[pp[i]]++;
			ans+=num[n-pp[i]];	
		}

		printf("%I64d\n",ans);
	}
	return 0;
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 福州大学汇编语言期末考试历年来都是以实际操作为重点,考察学生对于汇编语言的运用能力和实践经验。考试内容主要包括单项选择、填空和编程题,其中单项选择和填空题主要考察学生对汇编语言基础知识的掌握程度,编程题则主要考察学生对实际问题的处理能力。 历年考试中,编程题主题涵盖了汇编语言的各个方面,如输入输出、数学运算、条件判断、循环控制和数组操作等,涉及到实际问题的解决和编程技巧的掌握。在编程题的设计中,难度逐年升高,难度适中,涵盖的知识点也更加广泛且实用。 此外,汇编语言期末考试还会考查学生对课程中实验的理解和应用,以及对常用工具集成开发环境MASM和汇编语言程序设计流程的掌握情况。这样的设计旨在促进学生在实际运用中理解和掌握汇编语言的基本原理,提高汇编语言程序设计的能力。 总之,福州大学汇编语言期末考试历年以实际操作为主要考核方式,通过提供各种实际问题设计编程题目,考察学生对汇编语言基础知识和实践经验的掌握情况,以此检验学生的实际运用能力和应用能力。 ### 回答2: 福州大学计算机系汇编语言是一门重要的计算机基础课程,教授学生如何理解计算机内部运行机制。该课程通常在每个学期末会进行考试,最终计入学生的总成绩中。 历年来,fzu汇编语言期末考试的难度相对较高,因此学生需要花费充分的时间和精力去复习,了解考试方向和内容。在考试中,学生需要熟悉汇编语言的基本概念和常用指令,能够写出程序并进行调试。 在考试内容上,历年来的考试题目都涵盖了汇编语言的总体知识点,例如CPU结构、数据存储方式、寻址方式、编程方法、中断和I/O等。考试题型包括选择题、填空题、简答题和编程题等多种形式。 考试中的复习重点包括:CPU基本结构和运行原理、汇编语言常用指令、寻址方式和程序调试方法等。由于该课程是计算机系的入门课程,因此对于学生后续学习计算机领域的课程和研究都至关重要。 总之,fzu汇编语言期末考试历年来难度较大,需要学生投入足够的时间和精力复习考试内容。但是学好这门课程对于学生通向计算机领域和进行相关领域研究具有极为重要的作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值