1. 程式人生 > >【整理】分塊在數論中的運用(初稿)(各位幫幫忙填下坑,裏面列的好多都不會)

【整理】分塊在數論中的運用(初稿)(各位幫幫忙填下坑,裏面列的好多都不會)

body element set max main lucas定理 們的 prev 成了

之前已經做過幾個分塊的水題,導致nmphy居然口出狂言:“高中學過,簡單”。(現在nmphy收回他的話,並且跪著寫下這篇總結)

前言:

  1. 由於數論模運算中常常遇到一些問題:比如long long溢出,需要把乘法改為快速乘法(和快速冪是一個道理)。
  2. 註意邊界,0,1,什麽的。
  3. 運用函數時防止需要精度誤差,如sqrt,log2()等等(因為這個,昨天的CF第二題第48個點就被hack了,可以去感受CF456B一下)
  4. 。。。

α,組合數取模組合數取模根據n,m,p規模不同,分別有不同的解決方法。

  1. 數據小的就不說了,暴力也行,唯一分解
    也o98k。
  2. n,m <= 10^100,p <= 10^5,p是素數 。Lucas定理
  3. n,m <= 10^100,p <= 10^5,p是合數,且分解後因子不太多。 Lucas定理+中國剩余定理。
  4. n,m <= 10^100,p <= 10^9, p是素數,好像涼涼。

第四個這怎麽搞呢?這個時候……貌似跟3差不多。但是……需註意一個問題, c(n % p,m % p)我們在做Lucas 定理的時候需要預處理(得到階乘),此時P也很大, 預處理不出來。方法:分塊打表

。只保存sqrt(p)個 n!的值,c(n,m) = n! / (m! * (n – m)!),P是素數,可以直接乘法逆元。所以時間就是 logP * sqrt(P)。具體的博主日後補充。(畢竟nmphy也沒寫過)

β,階乘除階乘:

1:(n! / m!)% p 怎麽求, n,m <= 10^10,p <= 10^6,p是素數 。

顯然不能用Lucas,畢竟不是組合數。再看n,m是如此的大,怎麽搞? 這個我們轉化成乘法逆元來做

  1. ) 令 n! = (p^a) * u, m! = (p^b) * w
  2. ) 顯然a >= b。 若a > b 此時 n! / m! 是p的倍數。 那麽余數為0。
  3. ) 若a == b,此時我們只需要算u / w。由於w與p互質,u / w可以直接算u*w的逆元。
  4. ) 現在開始討論如何計算u/w n! = 1 * 2 * … * (p – 1) * 1p * (p + 1) * (p + 2) * … (2p – 1) * 2p * … (kp + 1) * (kp + 2) * .. (kp – 1) * kp * … ((k +1)p + 1) * ((k + 1)p + 2) * .. ((k +1)p + t)

其中,K = n/ p, t = n!% p

註意到 1和(p + 1) , 2和(p+2)…都是同余的。 所以我們的式子可以化成: ((p – 1)!)^k * t! * k! * p^k,k!哪裏出現的?

註意到 1p,2p..kp P^k我們提取出來,(p – 1)!和t!可以預處理,現在需要處理的是k!即(n/p)!,所以我們現在問 題轉化成計算k!,那麽遞歸下去即可。

2: 現在講講n,m <= 10^9,p <= 10^6, p不是素數怎麽做

首先,我們應該把p分解成pi^ci的形式。 直奔主題,討論如何把n!分解掉。

首先,我們把1..n中p的倍數提取出來,那麽由於mod p^c. 所以提取後會有若幹段循環:1 * 2 * … * (p – 1) * (p + 1) * (p + 2) *… *(p^c – 1) 令k = n / (p^c), t = n % (p^c),kk = n / p N!= P[p^c – 1]^k * P[t] * p^kk * kk! 註意這裏的P[p^c-1]是沒有把p的倍數乘進來。

同上,只要遞歸計算kk!(相關題目:spoj sequence

γ,高次剩余問題:

a^x = b(mod p)

  1. 知道a,x,p,求b (100%有解)
  2. 知道a,b,p,求x (有可能無解)
  3. 知道x,b,p,求a (依然有可能無解)
  4. 知道a,x,p,求某區間範圍內的p(持續有可能無解)

對2,3類問題我們還可以分別進行升級,求解的個數…) 從1到4,求解的難度是越來越大的.

這裏只講第二個和分塊的關系:BSGS算法。

BSGS算法(北上廣深?拔山蓋世?): (參考,http://blog.csdn.net/Clove_unique/article/details/50740412)

先看假如暴搜,其枚舉範圍:

根據費馬小定理:a^(p1)1(modp)
如果x已經枚舉到了p-1了,繼續枚舉的話就會產生循環。
所以,在暴搜中x的枚舉範圍就是0……p-1。

試想分塊如何優化暴搜:

我們想一想可不可以用分塊來解決枚舉的x。
把x分成p1−−−−√分別枚舉行不行?
設m=p1−−−−√y=am-b,這樣枚舉a和b就相當於分塊枚舉了。
那麽現在就變成了x^(am-b)z(modp)
把a和b分別放在兩邊:x^b*zx^(am)(modp)
我們可以發現左邊的x^b最多只有m個,完全可以預處理出來放進hash裏面。

第一代醜代碼://4000多ms,主要是用map來哈希,根本沒有體現到BSGS以空間換時間的優勢。

技術分享圖片
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<map>
#define ll long long
using namespace std;
ll a,b,p;
map<ll,int>mp;
void solve()
{
    if(a%p==0) {
        printf("no solution\n");return ;
    }  b%=p;   a%=p;
    ll tmp=b;
    ll block=sqrt(p-1)+1;//向上取整 
    if(mp[tmp]==0) mp[tmp]=0;
    for(int j=1;j<=block;j++){
        tmp=tmp*a%p;
        mp[tmp]=j;
    }
    ll tmpa=1,m=block;
    while(m){
         if(m&1) tmpa=tmpa*a%p;
         a=a*a%p;
         m/=2;
    }
    ll tmpb=1;
    for(int i=1;i<=block;i++){
        tmpb=tmpb*tmpa%p;
        if(mp[tmpb]!=0){
            printf("%lld\n",((i*block-mp[tmpb])%p+p)%p);
            return ;
        }
    }
    printf("no solution\n");
    return ;
}
int main()
{
    while(~scanf("%lld%lld%lld",&p,&a,&b)){
        mp.clear();
        solve();
    } return 0;
}
View Code

修進後的模板代碼://30ms

技術分享圖片
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
const int maxn=76543;
int To[maxn],Laxt[maxn],Next[maxn],id[maxn],cnt;
void insert(int x,int y){
    int k=x%maxn;
    Next[++cnt]=Laxt[k];
    Laxt[k]=cnt;
    id[cnt]=y;
    To[cnt]=x;
}
int find(int x){
    int k=x%maxn;
    for(int i=Laxt[k];i;i=Next[i]){
        if(To[i]==x) return id[i];
    } return -1;
}
int BSGS(int a,int b,int p){
    memset(Laxt,0,sizeof(Laxt));cnt=0;
    if(a%p==0||b%p==0) return -1;
    if(b==1) return 0;
    int m=sqrt(1.0*p)+1,j;
    ll x=1,np=1;
    for(int i=0;i<m;i++,np=np*a%p) insert(np*b%p,i);
    for(ll i=m;;i+=m){
        if((j=find(x=x*np%p))!=-1) return i-j;
        if(i>p) break;
    }
    return -1;
}
int main()
{
    int a,b,p;
    while(~scanf("%d%d%d",&p,&a,&b)){
        int ans=BSGS(a,b,p);
        if(ans==-1) printf("no solution\n");
        else printf("%d\n",ans);
    } return 0;
}
View Code

先放道題在這裏,https://vjudge.net/problem/CodeForces-830C ,之前遇到的,不知道和這個有關沒,等我肝完博客在看看

【參考】: 成都七中 2013級13班 王 迪 《信息學中的分塊思想》

【整理】分塊在數論中的運用(初稿)(各位幫幫忙填下坑,裏面列的好多都不會)