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;
}
View Code

 

posted @ 2019-08-08 13:27  darrrr  阅读(152)  评论(0编辑  收藏  举报