2024上海大學生程序設計競賽I-六元組計數原根知識詳解

以前基本沒有了解原根相關的一塊內容,最近正好碰到了這個題,于是寫一篇博客記錄一下。

在這里插入圖片描述

這道題的總體思路就是比較明顯,就是先算出 a p = x a^p=x ap=x對于每個 x x x的解的個數,然后NTT算一下即可。

先來講一下怎么求歐拉函數 ? ( x ) \phi (x) ?(x),即1到 x ? 1 x-1 x?1中與 x x x互素的數的個數,我們一般在線性篩里面通過遞推來求出這個函數。也就是我們要找出 ? ( i ? p [ j ] ) \phi(i*p[j]) ?(i?p[j]) ? ( i ) , ? ( p [ j ] ) , i , p [ j ] \phi (i),\phi(p[j]),i,p[j] ?(i),?(p[j]),i,p[j]之間的關系。我們把與 ? ( i ) \phi(i) ?(i)所代表的集合寫在一行里面,然后第二行寫上前一行的數加上 i i i,一直寫 p [ j ] p[j] p[j]行。

現在舉兩個例子。

例1:

i%p[j]!=0
p[j]=3,i=8//雖然實際不會出現這種,但是對于證明來講問題不大
1  3  5  7
9  11 13 15
17 19 21 23然后phi[24]表示的集合為
1       5   711  13
17 19       23  

例2:

i%p[j]==0
p[j]=2,i=10
1  3  7  9
11 13 17 19
phi[i*p[j]]=8

在例1中,因為 i m o d p [ j ] ≠ 0 i \mod p[j] \ne 0 imodp[j]=0,也就是 ? ( i ) \phi(i) ?(i)所表示的集合中存在 p [ j ] p[j] p[j]的倍數。然后你可以發現,每一列里面恰好就有一個是 p [ j ] p[j] p[j]的倍數,我們不妨設某一列第一個數為 x x x,且 y = x m o d p [ j ] y=x\mod p[j] y=xmodp[j],那么因為 p [ j ] p[j] p[j]為質數,所以 y m o d p [ j ] , y + i m o d p [ j ] , y + 2 i m o d p [ j ] , . . . , y + ( p [ j ] ? 1 ) i m o d p [ j ] y\mod p[j],y+i\mod p[j],y+2i\mod p[j],...,y+(p[j]-1)i\mod p[j] ymodp[j],y+imodp[j],y+2imodp[j],...,y+(p[j]?1)imodp[j] 0 , 1 , . . . , p [ j ] ? 1 0,1,...,p[j]-1 0,1,...,p[j]?1恰好每個一個,因此可以知道 p h i [ i ? p [ j ] ] = p h i [ i ] ? ( p [ j ] ? 1 ) ( i m o d p [ j ] ≠ 0 ) phi[i*p[j]]=phi[i]*(p[j]-1)(i \mod p[j] \ne 0) phi[i?p[j]]=phi[i]?(p[j]?1)(imodp[j]=0)

在例2中,因為 i m o d p [ j ] = 0 i\mod p[j] =0 imodp[j]=0,因此 ? ( i ) \phi(i) ?(i)所表示的集合里面沒有 p [ j ] p[j] p[j]的倍數,因此第一行里面沒有被刪掉的,因為相鄰兩行之間的差都是 p [ j ] p[j] p[j]的倍數,所以不會有任何的數為 p [ j ] p[j] p[j]的倍數。

然后就是原根部分的知識。

如果 x x x是模 P P P意義下的原根,那么 1 , 2 , . . . , P ? 1 1,2,...,P-1 1,2,...,P?1中每個數都能寫成 x t ( 0 < = t < = P ? 2 ) x^t(0<=t<=P-2) xt(0<=t<=P?2)的形式,把0的特殊情況處理掉,然后我們就可以把乘法轉換成加法,也就是求 i ? j = x m o d ( P ? 1 ) ( 0 < = i < = P ? 2 , 1 < = j < = N ) i*j=x\mod (P-1)(0<=i<=P-2,1<=j<=N) i?j=xmod(P?1)(0<=i<=P?2,1<=j<=N)每個 x x x的解。

到這一步以后我就卡了很久,因為不知道怎么進一步去求了,雖然和 P ? 1 P-1 P?1公因數相同的數會產生一樣的循環節,但是如果 N N N不是 P ? 1 P-1 P?1的倍數就無從下手了,因此我感覺自己還有什么性質沒有發現,然后我就不停的打表觀察。

直到我打表直接求出每個 x x x的解的時候,我發現無論 N N N怎么取,和 P ? 1 P-1 P?1公因數相同的數的解的個數是一樣的,那么我們就可以枚舉 P ? 1 P-1 P?1的每個因子,然后暴力枚舉每個 i i i有多少個解,這樣時間復雜度是 O ( P P ) O(P\sqrt P) O(PP ?),理論上是可以過的,雖然有點不太優秀。然后我就開始思考其背后的原因。

假設 N N N是不斷增大的, N N N每增大 1 1 1,這個結果都能保持不變,也就是說對于任意一個 j j j, i ? j ( 0 < = i < = P ? 2 ) i*j(0<=i<=P-2) i?j(0<=i<=P?2)都能滿足和 P ? 1 P-1 P?1公因數相同的數出現個數一樣(固定 j j j,變化 i i i)。

然后我們嘗試理解一組數乘完某個數以后仍然在同一組。顯然 j = 1 j=1 j=1時,即 0 , 1 , 2 , . . . , P ? 2 0,1,2,...,P-2 0,1,2,...,P?2種顯然滿足這個條件,那么我們把相同公因數的數放到同一組里面去,考慮一組里面的數同時乘上同一個數會怎么樣。結論是要么這個公因數保持不變,要么翻倍。不妨設某組數和 P ? 1 P-1 P?1的最大公因數為 d d d,這一組里面某個數為 a d ad ad,然后乘以一個 k k k。那么 g c d ( k a d m o d P ? 1 , P ? 1 ) gcd(kad\mod P-1,P-1) gcd(kadmodP?1,P?1)就為 g c d ( k a d , P ? 1 ) gcd(kad,P-1) gcd(kad,P?1),這就是輾轉相除法,因為 g c d ( a , P ? 1 ) = 1 gcd(a,P-1)=1 gcd(a,P?1)=1,所以 g c d ( k a d , P ? 1 ) = g c d ( k d , P ? 1 ) gcd(kad,P-1)=gcd(kd,P-1) gcd(kad,P?1)=gcd(kd,P?1)。特別地,如果 g c d ( k a d , P ? 1 ) = P ? 1 gcd(kad,P-1)=P-1 gcd(kad,P?1)=P?1,結果就是0。

然后我們還要弄明白一點,就是同一組數乘完某個數以后雖然仍然在同一組,那么是不是那一組里面每個數的個數都是相同的,答案是肯定的。首先證明,對于全部與 P ? 1 P-1 P?1互素的數,如果同時乘上一個與 P ? 1 P-1 P?1互素的數,那么乘完以后還是原來的那些數,只是順序發生了改變。設原來兩個數為 x , y ( x ≠ y ) ,那么 k x ? k y = k ( x ? y ) ≠ 0 ( m o d P ? 1 ) x,y(x\ne y),那么kx-ky=k(x-y) \ne 0(mod\ P-1) x,y(x=y),那么kx?ky=k(x?y)=0(mod?P?1),也就是兩兩不同。然后我們先考慮 g c d ( x , P ? 1 ) gcd(x,P-1) gcd(x,P?1) 1 1 1的組,并且乘的 k k k P ? 1 P-1 P?1的因子的情況,然后就可以根據剛剛證明的性質把全部情況轉換成這種情況,也就是先要提最大公因數,轉換成互質,然后把乘的數拆成兩部分,一部分互質,一部分是因子,前一部分不會改變數的值,只會改變順序,后一部分就是特殊的情況。現在來考慮這種特殊情況,再次強調 k ∣ P ? 1 k|P-1 kP?1,那么我們令 t = P ? 1 k t=\frac{P-1}{k} t=kP?1?,如果兩個數 x , y x,y x,y(其中 g c d ( x y , P ? 1 ) = 1 ) gcd(xy,P-1)=1) gcd(xy,P?1)=1)滿足 k x = k y ( m o d P ? 1 ) kx=ky(mod\ P-1) kx=ky(mod?P?1),那么有 x = y ( m o d t ) x=y(mod\ t) x=y(mod?t),這個問題就能轉化成,所有與 P ? 1 P-1 P?1互素的數在模 t t t意義下是不是每個數出現次數相同。想證明這個可以用到前面求歐拉函數的方法,我們不妨考慮和 t t t互素的每個數對應多少個和 P ? 1 P-1 P?1互素的個數。因為 k = P ? 1 t k=\frac{P-1}{t} k=tP?1?,然后我們就可以把 k k k拆成若干個質數相乘,每乘一個質數就用一次求歐拉函數的方法(同一列都是對應同一個與 t t t互素的數),就能知道每個與 t t t互素的數在每次操作以后對應的數的個數相同,這樣就能證明這個性質了。

在具體實現的時候,我采用了先枚舉每個 P ? 1 P-1 P?1的因子 i i i,然后遍歷整個周期 p e r i o d = P ? 1 i period=\frac{P-1}{i} period=iP?1?,這樣的復雜度是小于 p l o g p \sqrt plogp p ?logp的(但是要先預處理出每個數和 P ? 1 P-1 P?1的最大公因數,否則要多一個log)。因為這是全部因子之和,而我們知道因子和的計算方式為 ( 1 + p 1 + p 1 2 + . . . + p 1 c 1 ) ( 1 + p 2 + p 2 2 + . . . + p 2 c 2 ) . . . ( 1 + p n + p n 2 + . . . + p n c n ) = ∏ i = 1 n p i c i + 1 ? 1 p i ? 1 < = ∏ i = 1 n p i c i + 1 p i ? 1 = ∏ i = 1 n p i c i ∏ i = 1 n p i p i ? 1 < = ( P ? 1 ) ∏ i = 1 n i + 1 i = ( n + 1 ) ( P ? 1 ) < = ( P ? 1 ) l o g ( P ? 1 ) (1+p_1+p_1^2+...+p_1^{c_1})(1+p_2+p_2^2+...+p_2^{c_2})...(1+p_n+p_n^2+...+p_n^{c_n})=\prod_{i=1}^n\frac{p_i^{c_i+1}-1}{p_i-1}<=\prod_{i=1}^n\frac{p_i^{c_i+1}}{p_i-1}=\prod_{i=1}^np_i^{c_i}\prod_{i=1}^n\frac{p_i}{p_i-1}<=(P-1)\prod_{i=1}^n\frac{i+1}{i}=(n+1)(P-1)<=(P-1)log(P-1) (1+p1?+p12?+...+p1c1??)(1+p2?+p22?+...+p2c2??)...(1+pn?+pn2?+...+pncn??)=i=1n?pi??1pici?+1??1?<=i=1n?pi??1pici?+1??=i=1n?pici??i=1n?pi??1pi??<=(P?1)i=1n?ii+1?=(n+1)(P?1)<=(P?1)log(P?1)

然后我們可以找出一個原根,算出每個數是原根的幾次方,然后就能得到每個數出現的次數。然后用ntt卷積一次即可。

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
#define ll long long
using namespace std;
template<typename T>inline void qr(T &x){x=0;int f=0;char s=getchar();while(!isdigit(s))f|=s=='-',s=getchar();while(isdigit(s))x=x*10+s-48,s=getchar();x=f?-x:x;
}
int cc=0,buf[31];
template<typename T>inline void qw(T x){if(x<0)putchar('-'),x=-x;do{buf[++cc]=int(x%10);x/=10;}while(x);while(cc)putchar(buf[cc--]+'0');
}
const int N=1e5+10,mod=998244353;
namespace NTT{const int G=3,Gi=332748118;int n,m;int lim=1,cnt;int pos[N<<2];int a[N<<2],b[N<<2];int power(int x,int y){int ret=1;while(y){if(y&1)ret=1ll*ret*x%mod;x=1ll*x*x%mod;y>>=1;}return ret;}void ntt(int *A,int type){rep(i,0,lim-1)if(i<pos[i])swap(A[i],A[pos[i]]);for(int mid=1;mid<lim;mid<<=1){int Wn=power(type==1?G:Gi,(mod-1)/(mid<<1));for(int R=mid<<1,j=0;j<lim;j+=R){int w=1;for(int k=0;k<mid;k++,w=1ll*w*Wn%mod){int x=A[j+k],y=1ll*w*A[j+mid+k]%mod;A[j+k]=(x+y)%mod;A[j+k+mid]=(x-y+mod)%mod;}}}}void work(){while(lim<=n+m)lim<<=1,cnt++;rep(i,0,lim-1)pos[i]=(pos[i>>1]>>1)|((i&1)<<(cnt-1));ntt(a,1),ntt(b,1);rep(i,0,lim)a[i]=1ll*a[i]*b[i]%mod;ntt(a,-1);int inv=power(lim,mod-2);rep(i,0,n+m)a[i]=1ll*a[i]*inv%mod;}
}
int P,n;
int cnt,p[N],phi[N];bool v[N];
int sum[N],s[N];
int pre_gcd[N];
int pos[N];
int num[N];
int gcd(int x,int y){return !y?x:gcd(y,x%y);}
void solve(){qr(P),qr(n);rep(i,2,100000){if(!v[i])p[++cnt]=i,phi[i]=i-1;for(int j=1;j<=cnt&&i*p[j]<=100000;j++){v[i*p[j]]=1;if(i%p[j]==0){phi[i*p[j]]=phi[i]*p[j];break;}else phi[i*p[j]]=phi[i]*(p[j]-1);}}rep(i,2,P-1){int now=1;bool bk=1;rep(j,1,P-1){now=1ll*now*i%P;if(now==1&&j!=P-1){bk=0;break;}pos[now]=j;}if(bk)break;}rep(i,1,P-2){if(gcd(i,P-1)==1)s[i]++;s[i]+=s[i-1];}rep(i,0,P-1)pre_gcd[i]=gcd(i,P-1);sum[P-1]=n%mod;//gcd(x,P-1)=irep(i,1,P-2)if((P-1)%i==0){int period=(P-1)/i;int group_siz=phi[(P-1)/i];rep(j,1,period){if(j==period){(sum[P-1]+=1ll*group_siz*(n/period)%mod)%=mod;break;}int now=pre_gcd[i*j];int p1=n/period%mod,p2=n%period;int group_siz2=phi[(P-1)/now];(sum[now]+=1ll*(group_siz/group_siz2)*(p1+(j<=p2))%mod)%=mod;}}num[0]=n%mod;rep(i,1,P-1)num[i]=sum[gcd(pos[i],P-1)];NTT::n=NTT::m=P-1;rep(i,0,P-1)NTT::a[i]=NTT::b[i]=num[i];NTT::work();int ans=0;rep(i,0,2*(P-1))(ans+=1ll*NTT::a[i]*num[i%P]%mod)%=mod;qw(ans);
}
int main(){int tt;tt=1;while(tt--)solve();return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/39675.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/39675.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/39675.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

前端FCP指標優化

優化前 第三方依賴按需引入之后&#xff0c;打包的總體積減小到初始值的55%&#xff0c;但是依然存在很大的js文件&#xff0c;需要繼續優化 chunk-vendors.js進行分包之后 截圖 compression-webpack-plugin壓縮之后 截圖

簡單制作基礎的Python鏡像

拉取基礎鏡像 以Ubuntu24示例 docker pull ubuntu:24.04 啟動 docker run -it -d --name ubuntu24 ubuntu:24.04 進入docker docker exec -it ubuntu24 /bin/bash 更新依賴 apt update apt full-upgrade 安裝pip 會自動安裝python3.11.7 apt install pip 支持中文…

55、Flink 中使用 Java Lambda 表達式詳解

1&#xff09;概述 1.注意 Flink 支持對 Java API 的所有算子使用 Lambda 表達式&#xff0c;但是&#xff0c;當 Lambda 表達式使用 Java 泛型時&#xff0c;需要 顯式 地聲明類型信息。 2.示例和限制 示例&#xff1a; map() 函數使用 Lambda 表達式計算輸入值的平方。 …

大學新生人工智能學習路線規劃

1. 引言 七月來臨&#xff0c;各省高考分數已揭榜完成。而高考的完結并不意味著學習的結束&#xff0c;而是新旅程的開始。對于有志于踏入IT領域的高考少年們&#xff0c;這個假期是開啟探索IT世界的絕佳時機。作為該領域的前行者和經驗前輩&#xff0c;我愿意為準新生們提供一…

基于Hadoop平臺的電信客服數據的處理與分析③項目開發:搭建基于Hadoop的全分布式集群---任務10:Hive安裝部署

任務描述 任務內容為安裝并配置在Hadoop集群中使用Hive。 任務指導 Hive是一個基于Hadoop的數據倉庫框架&#xff0c;在實際使用時需要將元數據存儲在數據庫中 具體安裝步驟如下&#xff1a; 1. 安裝MySQL數據庫&#xff08;已安裝&#xff09; 2. 解壓縮Hive的壓縮包 3…

剪映 v5.5 Pro Vip解鎖版:使用指南與注意事項

摘要&#xff1a;本文介紹了剪映Pro VIP解鎖版的使用方法&#xff0c;包括安裝、測試和使用VIP素材的步驟&#xff0c;以及如何避免誤報和保持解鎖狀態的建議。 正文&#xff1a; 剪映Pro是一款廣受歡迎的視頻編輯軟件&#xff0c;提供了豐富的視頻編輯功能和大量高質量的素材…

發送微信消息和文件

參考&#xff1a;https://www.bilibili.com/video/BV1S84y1m7xd 安裝&#xff1a; pip install PyOfficeRobotimport PyOfficeRobotPyOfficeRobot.chat.send_message(who"文件傳輸助手", message"你好&#xff0c;我是PyOfficeRobot&#xff0c;有什么可以幫助…

RabbitMQ中java實現隊列和交換機的聲明

java實現隊列和交換機的聲明 在之前我們都是基于RabbitMQ控制臺來創建隊列、交換機。但是在實際開發時&#xff0c;隊列和交換機是程序員定義的&#xff0c;將來項目上線&#xff0c;又要交給運維去創建。那么程序員就需要把程序中運行的所有隊列和交換機都寫下來&#xff0c;…

【PYG】 PyTorch中size方法和屬性

在 PyTorch 中&#xff0c;size 方法和屬性用于獲取張量的維度信息。下面是它們的用法和區別&#xff1a; node_features.size&#xff1a; 這是一個屬性&#xff08;attribute &#xff09;&#xff0c;返回一個 torch.Size 對象&#xff0c;表示張量的維度。這是不可調用的&a…

用MySQL+node+vue做一個學生信息管理系統(一):配置項目

先用npm init -y生成配置文件 在項目下新建src文件夾&#xff0c;app.js文件。src目錄用來放靜態資源文件&#xff0c;app.js是服務器文件&#xff0c;index.js是vue的入口文件 使用npm install express下載express框架 在app.js文件夾開啟node服務&#xff0c;監聽的端口為…

C++ //練習 14.29 為什么不定義const版本的遞增和遞減運算符?

C Primer&#xff08;第5版&#xff09; 練習 14.29 練習 14.29 為什么不定義const版本的遞增和遞減運算符&#xff1f; 環境&#xff1a;Linux Ubuntu&#xff08;云服務器&#xff09; 工具&#xff1a;vim 解釋&#xff1a; 遞增和遞減要改變對象本身&#xff0c;const類…

Go語言--運算符

算術運算符 關系運算符 不能寫0<a<10&#xff0c;要判斷必須0<a&&a<10。因為int和bool不兼容 邏輯運算符 位運算符 賦值運算符 其他 運算符的優先級

AcWing 1254:找樹根和孩子

【題目來源】https://www.acwing.com/problem/content/1256/【題目描述】 給定一棵樹&#xff0c;輸出樹的根root&#xff0c;孩子最多的結點max以及他的孩子。【輸入格式】 第一行&#xff1a;n&#xff0c;m&#xff0c;表示樹的節點數和邊數。 以下m行&#xff1a;每行兩個結…

浮點數在內存中的存儲結構

浮點數在內存中的存儲可以參考《IEEE754標準》https://people.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF 參考博文&#xff1a;IEEE754詳解&#xff08;最詳細簡單有趣味的介紹&#xff09;-CSDN博客 單精度float占內存4字節&#xff0c;最高位bit31表示符號位&…

國家海岸線變化評估:新英格蘭和中大西洋沿岸海岸線的歷史變化

National Assessment of Shoreline Change: Historical Shoreline Change along the New England and Mid-Atlantic Coasts 國家海岸線變化評估&#xff1a;新英格蘭和中大西洋沿岸海岸線的歷史變化 摘要 海灘侵蝕是美國許多公海沿岸的一個長期問題。隨著沿岸人口的不斷增加…

永輝超市購物卡有什么用?

感覺現在在超市買東西&#xff0c;還不如網購 這不&#xff0c;端午的時候&#xff0c;朋友送的永輝卡&#xff0c;一直沒時間去用&#xff0c;我總擔心過期 但是去了超市后&#xff0c;又不知道買什么&#xff0c;最后空手而歸 還好收卡云可以回收永輝卡&#xff0c;兩張三…

《C++20設計模式》適配器模式經驗分享

文章目錄 一、前言二、對于接口的討論三、實現1、對象適配器1.1 UML類圖1.2 實現 2、類適配器 四、最后 一、前言 從適配器模式開始就是類的組合聚合&#xff0c;類與類之間結構性的問題了。 適配器模式解決的問題&#xff1a; 適配器模式能夠在不破壞現有系統結構的情況下&a…

mapreduce實現bean的序列化與反序列化

目錄 序列化&#xff08;Serialization&#xff09; 反序列化&#xff08;Deserialization&#xff09; 事例操作 UserSale 重寫序列化方法 重寫反序列化 重寫toString方法 SaleMapper SaleReducer SaleDriver 序列化&#xff08;Serialization&#xff09; 序列化是將…

【后端面試題】【中間件】【NoSQL】MongoDB的配置服務器、復制機制、寫入語義和面試準備

MongoDB的配置服務器 引入了分片機制之后&#xff0c;MongoDB啟用了配置服務器(config server) 來存儲元數據&#xff0c;這些元數據包括分片信息、權限控制信息&#xff0c;用來控制分布式鎖。其中分片信息還會被負責執行查詢mongos使用。 MongoDB的配置服務器有一個很大的優…

WPF----自定義滾動條ScrollViewer

滾動條是項目當中經常用到的一個控件&#xff0c;大部分對外項目都有外觀的需求&#xff0c;因此需要自定義&#xff0c;文中主要是針對一段動態的狀態數據進行展示&#xff0c;并保證數據始終在最新一條&#xff0c;就是需要滾動條滾動到底部。 1&#xff0c;xaml中引入 <…