CF1030G Linear Congruential Generator

Link
显然每个元素的变化轨迹都是\(\rho\)型。
那么设第\(i\)个元素的轨迹的柄长为\(d_i\),圆周长为\(l_i\),那么不同的元组数量就是\(\max\limits_{i=1}^n(d_i)+\operatorname{lcm}\limits_{i=1}^n{l_i}\)
我们现在考虑\(a_i\)的取值对于\(d_i,l_i\)的影响,不难发现只需要考虑\(a_i,b_i,x_i\in[0,p_i)\)的情况。

\(1.a_i=0\)

很显然此时\(d_i=[b_i=x_i],l_i=1\)。因为我们希望\(d\)尽可能大,所以\(d_i=1\)

\(2.a_i=1\)

很显然此时\(d_i=0,l_i=p_i\)

\(3.a_i>1\)

很显然此时\(d_i=0\),而\(l_i=\min\{k\in\mathbb{N_+}|a_i^kx_i+b_i\sum\limits_{j=0}^{k-1}a_i^j\equiv x_i\pmod{p_i}\}\)
变形之后得到\((a_i^k-1)(x_i+\frac{b_i}{a_i-1})\equiv0\pmod{p_i}\)
第二个式子与\(k\)是无关的,因此若\(x_i+\frac{b_i}{a_i-1}\equiv0\pmod{p_i}\),那么\(l_i=0\),肯定不优。
因此\(l_i=\min\{k\in\mathbb{N_+}|a_i^k\equiv1\pmod{p_i}\}=p_i-1\)

我们称上述为选择\(1,2,3\)

因为\(d_i\le1\),因此我们可以先最大化\(\operatorname{lcm}\limits_{i=1}^n{l_i}\),再检查能否选择一个\(1\)
先考虑如何最大化\(\operatorname{lcm}\limits_{i=1}^n{l_i}\),我们降序枚举每个\(p_i\)同时维护答案\(s\),如果此时\(p_i\ne s\),那么最优的肯定是选择\(2\),否则选择\(3\)
具体而言维护每个质因子的最高次幂即可,为了方便之后检查能否选择\(1\),我们还需要记录有多少个\(l_i\)达到了这个最高次幂。
然后我们再次检查所有\(l_i\),如果它的所有质因子的最高次幂都有至少两个\(l_i\)达到了,那么我们将该\(l_i\)扔掉也不会对答案造成影响,这时候我们就可以将这个\(i\)改为选择\(1\)

#include<cctype>
#include<cstdio>
#include<algorithm>
const int N=2000007,P=1000000007;
int a[N],low[N],mx[N],num[N],vis[N];
int mul(int a,int b){return 1ll*a*b%P;}
int pow(int a,int k){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;}
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
void update(int p,int x){if(x>mx[p])mx[p]=x,num[p]=1;else num[p]+=mx[p]==x;}
int check(int x)
{
    for(int p,c;x^1;)
    {
	for(p=low[x],c=0;!(x%p);x/=p,++c);
	if(c==mx[p]&&num[p]==1) return 0;
    }
    return 1;
}
int main()
{
    int n=read(),s=1,t=0;
    for(int i=1;i<=n;++i) a[i]=read();
    std::sort(a+1,a+n+1);
    for(int i=2;i<=a[n];++i) if(!low[i]) for(int j=i;j<=a[n];j+=i) if(!low[j]) low[j]=i;
    for(int i=n,v,x,p,c;i;--i)
	if(mx[v=a[i]]) for(vis[i]=1,x=v-1;x^1;s=mul(s,pow(p,std::max(0,c-mx[p]))),update(p,c)) for(p=low[x],c=0;!(x%p);x/=p,++c);
	else update(v,1),s=mul(s,v);
    for(int i=1;i<=n;++i) if((vis[i]&&check(a[i]-1))||(!vis[i]&&check(a[i]))) t=1;
//  for(int i=1;i<=n;++i) if(vis[i]&&check(a[i]-1)) t=1;
//  这里这么写判断条件也是没有问题的,分析易得其正确性。
    printf("%d",(s+t)%P);
}
posted @ 2020-04-10 21:12  Shiina_Mashiro  阅读(315)  评论(0编辑  收藏  举报