整理分块在数论中的运用(初稿)(各位帮帮忙填下坑,里面列的好多都不会)

Posted hua-dong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了整理分块在数论中的运用(初稿)(各位帮帮忙填下坑,里面列的好多都不会)相关的知识,希望对你有一定的参考价值。

之前已经做过几个分块的水题,导致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班   王 迪 《信息学中的分块思想》

 

以上是关于整理分块在数论中的运用(初稿)(各位帮帮忙填下坑,里面列的好多都不会)的主要内容,如果未能解决你的问题,请参考以下文章

如何运用KEGG数据库查询信号通路(入门版)

数论分块 数学

数论分块之整除分块

Fear Factoring(数论,除法分块)

Fear Factoring(数论,除法分块)

Fear Factoring(数论,除法分块)