8.7-Day1T1
题目大意:
T组测试数据,每组测试数据给出一个n,求[0,n-1]所有逆元的和。(n可能不为质数)
题解:
我的想法:
求出每一个数的逆元,再相加。由于有n为质数的时候,所以,我将它分为两种情况:(1)n为质数是,线性求逆元(2)n不为质数时,扩展欧几里得求逆元
理论上可以拿个60分的...但我数组开大了,,,文件直接不可运行了...爆零...
正解:
算法1:
当n是质数的时候,所有数都有质数,直接输出n * (n - 1) / 2;
算法2:
当n不是质数的时候,判断一下逆元是否存在,然后累加即可;
判断存在用 gcd(i,n)=1 就可以, O(logn);
求逆元可以利用扩展欧几里得求逆元,O(logn)
算法3:
这道题有两个性质:
性质 1:集合{[0,n-1]中存在逆元的数}==集合{[0,n-1]中存在逆元的数的逆元}
性质 2:[0,n-1]中与互质的数的和为 n*(n)/2 ;
性质 1 的正确性:
因为对逆元再求逆元得到的是本身,它们就是一一对应的了;
这样问题就转化成了求[0,n-1]中与 n 互质的数的和;
性质 2 是因为 gcd(n,i)=gcd(n,n-i);
所以与 n 互质的数可以关于 n/2 对称的,也就是相加等于 n;
那答案为 n*(n)/2 也是显然了;
复杂度为求欧拉的复杂度,O( sqrt(n) );
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define ll long long using namespace std; inline int read() { int sum = 0, p = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') p = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { (sum *= 10) += ch - '0'; ch = getchar(); } return sum * p; } int phi(int x) { int cnt = x; for(int i = 2; i * i <= x; i++) { if(x % i == 0) { cnt /= i; cnt *= i - 1; while(x % i == 0) x /= i; } } if(x != 1) cnt /= x,cnt *= x - 1; return cnt; } int main() { int n; int t = read(); while(t--) { n = read(); printf("%lld\n",(ll)n * phi(n) / 2); } return 0; }