gmoj 6832.world

Description

世界可以抽象成一个长度为偶数n 且元素互不相同的数列a。每当发生了一些意外,他都会相应的产生一些变化,并成为一个新的数列a′,并且它们满足以下关系:

如果这个数列正好经过n 次变换后首次回到最初始的数列a ,这个世界便是幸运的。此时的幸运值是n;如果没有回到最初始的序列,幸运值便是0。
那如果,这个序列的长度在[2,A] 之间的偶数内均匀随机时,请你告诉我,世界期望的幸运值是多少呢?

Input

输入一行一个正整数A,其意义见【题目描述】。

Outout

输出一行一个实数,表示我的幸运值的期望,请保留5 位小数。

Solution

对于第i个位置,经过k次变换后到达的位置应该是\(i\times2^k\ \%\ (n+1)\)

而对于第一个位置到达的的位置就是\(2^k\ \%(n+1)\)

所以回到第一个位置即\(2^k\equiv1(mod\ n+1)\)

所以现在要求的是这个最小的k

根据欧拉定理,\(2^{\phi(n+1)}\equiv1(mod\ n+1)\)

所以如果\(\phi(n+1)\) 小于n,则n就不会是最小的k

因为当n+1为质数是\(\phi(n+1)\) 才会是n,所以当n+1为质数时才判断这个n

但这个n不一定是最小的

当n不是最小的时候,最小的一定是n的因数(讲真挺好证的,画个图,因为\(y=2^x\ mod\ (n+1)\)这东西的y是循环的,所以要使刚好在n那等于1,则最小的那个数应该是n的因数)

把n分解质因数,最多就8个数,所以时间复杂度\(O(8n)\) 可以接受

Code

#include <cstdio>
#include <algorithm>
#include <cmath>
#define N 10000001
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;
int i,j,cnt,tot,n,x,ma,prime[N],a[N],last[N];
long long ans;
bool bz[N];
long long ksm(long long x,int y,int MO)
{
    long long sum=1;
    while (y)
    {
        if (y&1)sum=sum*x%MO;
        x=x*x%MO;
        y>>=1;
    }
    return sum;
}
int main()
{
    open("world");
    scanf("%d",&n);
    for (i=2;i<=n;i++)
    {
        if (!bz[i]) prime[++tot]=i;
        for (j=1;j<=tot;j++)
        {
            if (prime[j]*i>n) break;
            bz[prime[j]*i]=1;
            last[prime[j]*i]=prime[j];
            if (i%prime[j]==0) break;
        }
    }
    for (i=2;i<=n;i+=2)
    {
        if (bz[i+1]) continue;
        x=i;
        ma=0;
        if (ksm(2,i,i+1)==1) ma=i;
        while (last[x])
        {
            cnt=last[x];
            while (x%cnt==0) x/=cnt;
            if (ksm(2,i/cnt,i+1)==1) 
            {
            	ma=i/cnt;
            	break;
			}
        }
        if (x>1) 
		{
			if (!ma) ma=i/x;else
			if (ksm(2,i/x,i+1)==1 && ma==i) ma=i/x;
		}
        if (ma==i) ans+=i;
    }
    printf("%.5Lf",ans/(long double)(n/2));
    return 0;
}

posted @ 2020-10-24 19:53  Sport_River  阅读(81)  评论(0编辑  收藏  举报