[WC2020] 猜数游戏 题解

一个\(O(n^2\log)\)垃圾做法

考虑建出一张有向图\(G,\) 有边\(i->j\)当且仅当存在一个正整数\(m\)使得\(a_i^m\)\(a_j\)在模\(p\)意义下同余\(,\)然后在图\(G\)上计算答案\(.\)

Part1:建图

subtask1:模数为奇质数

\(p\)意义下存在原根\(,\)设为\(g.\)

那么每个\(a_i\)都可以表示成\(g^{k_i}\)

考虑求出一个数\(a_i\)的指标和\(\phi(p)\)\(\gcd\)的值\(k_i(k_i|\phi(p)):\)

显然\(k_i\)为满足\(a_i^{\phi(p)/k}\)\(p\)之后为\(1\)的最大的数\(.\)

枚举每个质因数\(p\)求它的次数\(,\)这个工作就可以在\(O(d(\phi(p)) \times \log p)\) 的时间内完成\(.\)

那么\(,\) \(i->j\)存在当且仅当\(k_i|k_j.\)

这样就可以\(O(1)\) \(check\)是否存在边\(i->j\)

subtask2:模数为奇质数\(q\)\(t(t>1)\)次幂

\(p^k\)意义下也存在原根\(.\)

不难发现我们可以用同样的方法求出所有\(k_i,\)但是这回一个数可能会是\(q\)的倍数\(,\)我们记\(a_i\)的因数分解中存在的\(q\)的个数为\(m_i.\)

这次我们怎么\(check\) \(i->j\)是否可以连边呢\(?\)

首先如果\(m_i=m_j=0,\)那么就直接用上一个\(subtask\)的做法即可\(.\)

如果有一个\(m=0,\)而另一个\(m ≠ 0,\)那么边\(i->j\)不存在\(.\)

如果\(m_i≠0,m_j≠0,\)那么我们可以直接计算出可能使\(a_i^z=a_j\)的数字\(z,z=m_j/m_i,\)然后直接快速幂解决\(.\)

那么就可以\(O(\log p)\)的复杂度内\(check\)是否存在边\(i->j\)

如果害怕\(T\)飞的话可以求出原根用光速幂\(,\)不过\(O(n^2\log p)\)实际情况下也能过\(.\)


Part2:计算答案

建出图之后\(,\)我们考虑怎么计算答案\(.\)

考虑一个\(a_i\)对答案的贡献\(.\)

有一个想法是\(a_i\)在答案中存在的条件为当且仅当没有任何数能够表示出它\(,\)记这些数的个数为\(cnt,\)那么\(a_i\)对答案的贡献就是\(2^{cnt}.\)

但是这样做忽略了环的情况\(,\)只能获得\(10pts.\)

举个例子\(,\)如果\(n=2,\)\(G\)中存在边\((1,2),(2,1),\)如果按照这种算法计算出来的答案就是\(2,\)而正确答案是\(3.\)

那么这个问题怎么解决呢\(?\)

不难发现如果有一个强联通分量\(S,\) \(S\)内部的所有点必然是能两两连边的\(,\)所以我们可以给一个强联通分量内的点强制一个顺序\(,\)就可以正确的计算出答案了\(.\)

代码\(:\) 见云剪贴板


Bonus:如何解决\(n\leq 10^5,p\leq 10^{18}\)

首先求出\(phi(p),\)\(pollard-rho\)分解质因数\(,\)并爆搜出\(phi(p)\)的因子\(.\)

对于\(\gcd(a_i,p)≠1\)\(a_i\)的贡献\(,\)

可以直接\(O(n\log p \times K)\)暴力解决\(,\)其中\(K\)\(phi(p)\)的质因数个数\(.\)

对于\(\gcd(a_i,p)=1\)的数字\(,\)

首先用\(O(n\log p \times K)\)的复杂度算出这\(O(n)\)个数的指标\(,\)

然后以这些因子为下标运行狄利克雷前缀和即可\(.\)

复杂度\(O(n\log p \times K + d(phi(p))\times K)\)

代码\(:\) 见云剪贴板

posted @ 2020-08-29 10:48  srf  阅读(207)  评论(0编辑  收藏  举报