zoukankan      html  css  js  c++  java
  • 【洛谷6730】[WC2020] 猜数游戏(数论)

    点此看题面

    • 给定一个长度为(n)的序列,其中每个数都在([1,p))范围内,且各不相同。
    • 规定当你选了序列中的某一个数(x),则所有(x^i\%p)都会被选中。
    • 定义一个序列的代价为选中整个序列至少需要选的数的个数,求给定序列所有子序列的代价之和。
    • (nle5 imes10^3,ple10^8)且为某个质数的幂

    建图:判断连边关系

    考虑我们暴力建出一张有向图,如果选中了(i)能选中(j),就连一条从(i)(j)的边。

    (p)是质数(pr)的幂,然后计算出(a_i=v_i imes pr^{k_i},a_j=v_j imes pr^{k_j})

    首先,如果(k_i,k_j)中一个为(0)一个不为(0),显然不可能。

    否则,如果(k_i,k_j)都不为(0),则当且仅当(a_i^{frac{k_j}{k_i}}equiv a_j(mod p))(i)才能向(j)连边。

    (k_i,k_j)都为(0)的情况就有些麻烦了。

    (a_i)为例,我们求出最大的(w_i)满足(a_i^{frac{phi(p)}{w_i}}equiv1(mod p)),相当于(frac{phi(p)}{w_i})就是(a_i)的最小周期。

    这一过程中只要枚举(phi(p))的所有质因子看看给(w_i)乘上这个质因子之后之后是否依然能满足条件就好了。注意对于每个(a_i)可以事先把(w_i)预处理出来。

    这样一来(i)能到(j)的充要条件就是(frac{phi(p)}{w_j}|frac{phi(p)}{w_i}),即(w_i|w_j)

    注意这个过程中要特判(a=1)的情况,可以假设它的(w)值为(0),表示任意数能到它,而它不能到任何数。

    答案的计算

    首先我们要知道,这道题中建出来的图非常特殊。

    考虑边的实际意义,就会发现如果(x)(y)有边,(y)(z)有边,则(x)(z)也必然有边。

    因此对于这道题中的一个强连通分量,它实际上是一个团。

    容易发现,对于一个团中的每对相反的有向边,给它强制定向(例如强制编号小的点向编号大的点连边),把整个团变成一个(DAG),在此题中实际上不会对答案造成任何影响。

    而此时每个点能产生贡献,当且仅当能到达它的点(必然直接有边相连)都没有被选择,言下之意也就是其他的点都可以任选(假设有(t)个),那么这个点的贡献就是(2^t)

    具体实现中只要知道边的存在情况,并不用真的建出图来。

    代码:(O(sqrt p+n^2logp))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 5000
    #define S 10000
    #define X 998244353
    using namespace std;
    int n,p,pr,Phi,a[N+5],w[N+5],k[N+5],pt,pp[S+5];
    I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
    I int QP(RI x,RI y,CI m) {RI t=1;W(y) y&1&&(t=1LL*t*x%m),x=1LL*x*x%m,y>>=1;return t;}
    I bool IsP(CI x) {if(x==1) return 0;for(RI i=2;i*i<=x;++i) if(!(x%i)) return 0;return 1;}
    I int Q(CI x)//对于a[x]求出最大的w[x]满足a[x]^(Phi/w[x])≡1
    {
    	if(x==1) return 0;RI i,t=1;//特判x=1
    	for(i=1;i<=pt;++i) QP(x,Phi/(t*pp[i]),p)==1&&(t*=pp[i]);return t;//尝试乘上每个质因子
    }
    I bool Check(CI i,CI j)//判断是否存在i到j的有向边
    {
    	if(!k[i]&&!k[j]) return w[i]&&!(w[j]%w[i]);if(!k[i]||!k[j]) return false;//两个都为0;一个为0一个不为0
    	return !(k[j]%k[i])&&QP(a[i],k[j]/k[i],p)==a[j];//两个都不为0,只有一种可能
    }
    int main()
    {
    	RI i,j;if(scanf("%d%d",&n,&p),IsP(p)) pr=p;else for(pr=2;p%pr;++pr);Phi=p/pr*(pr-1);//求出p是哪个质数的幂
    	RI x=Phi;for(i=2;i*i<=x;++i) if(!(x%i)) W(!(x%i)) pp[++pt]=i,x/=i;x^1&&(pp[++pt]=x);//分解质因数,注意重复质因数要存多次
    	for(i=1;i<=n;++i) if(scanf("%d",a+i),(x=gcd(a[i],p))==1) w[i]=Q(a[i]);else W(!(x%pr)) x/=pr,++k[i];//预处理每个数的信息
    	RI t,ans=0;for(i=1;i<=n;ans=(ans+QP(2,t,X))%X,++i)//求出不能到点i的点数t
    		for(t=0,j=1;j<=n;++j) i^j&&(!Check(j,i)||(i<j&&Check(i,j)))&&++t;//j不能到i,或i,j在团中且i的编号小于j的编号
    	return printf("%d
    ",ans),0;
    }
    
  • 相关阅读:
    LIBTIFF 配置 (vs2010 + win8 + 32位 )
    minGW、cygwin、gnuwin32 介绍
    LabVIEW发布功能总结
    LabVIEW新手5大错误
    专业功放测试:主要性能指标&信噪比测量
    常用低压电器的主要种类和用途
    LabVIEW是一种通用的编程语言吗?
    LabVIEW TCP/IP 断开重连问题
    LabVIEW 的bool(布尔)按键机械属性
    LabVIEW 远程控制VI
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6730.html
Copyright © 2011-2022 走看看