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

之前已经做过几个分块的水题,导致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^(p−1)≡1(modp)。 
          如果x已经枚举到了p-1了,继续枚举的话就会产生循环。 
          所以,在暴搜中x的枚举范围就是0……p-1。

试想分块如何优化暴搜:

我们想一想可不可以用分块来解决枚举的x。 
         把x分成p−1−−−−√分别枚举行不行? 
         设m=p−1−−−−√,y=a∗m-b,这样枚举a和b就相当于分块枚举了。 
         那么现在就变成了x^(a∗m-b)≡z(modp) 
         把a和b分别放在两边:x^b*z≡x^(a∗m)(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;
}

修进后的模板代码://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;
}

先放道题在这里,https://vjudge.net/problem/CodeForces-830C ,之前遇到的,不知道和这个有关没,等我肝完博客在看看。

【参考】:  成都七中   2013级13班   王 迪 《信息学中的分块思想》

原文地址:https://www.cnblogs.com/hua-dong/p/8213941.html

时间: 2024-08-28 00:29:40

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

ACM数论中相关定理(不断更新)

费马小定理是数论中的一个重要定理,其内容为: 假如p是质数,且(a,p)=1,那么 a^(p-1) ≡1(mod p).即:假如a是整数,p是质数,且a,p互质,那么a的(p-1)次方除以p的余数恒等于1. 费马大定理,又被称为“费马最后的定理”,由法国数学家费马提出.它断言当整数n >2时,关于x, y, z的方程 x^n + y^n = z^n 没有正整数解.被提出后,经历多人猜想辩证,历经三百多年的历史,最终在1995年被英国数学家安德鲁·怀尔斯证明. 中国剩余定理的结论: 令任意固定整数

【知识点整理】Oracle中NOLOGGING、APPEND、ARCHIVE和PARALLEL下,REDO、UNDO和执行速度的比较

[知识点整理]Oracle中NOLOGGING.APPEND.ARCHIVE和PARALLEL下,REDO.UNDO和执行速度的比较 1  BLOG文档结构图 2  前言部分 2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~: ① 系统和会话级别的REDO和UNDO量的查询 ② NOLOGGING.APPEND.ARCHIVE和PARALLEL下,REDO.UNDO和执行速度的比较(重点)   Tips: ① 本文

整理了一些.NET中的方案应用视频给大家

1.方案:ASP.NET开发RSS新闻发布订阅技术方案 http://edu.ibeifeng.com/view-index-id-162.html 2.方案:ASP.NET开发在线编辑器模块技术方案 http://edu.ibeifeng.com/view-index-id-161.html 3.方案:如何操作Excel文件带项目 http://edu.ibeifeng.com/view-index-id-159.html 4.方案:如何以流式方式读写文本文件 http://edu.ibeif

[整理]函数的声明中的数组参数

在函数的声明的数组参数中,并为指明数组的长度.因为在C中,允许单个函数操纵任意长度的一维数组.这个特性不利的一面是函数没法知道该数组的长度.如果确实需要知道数组的长度,必须作为一个单独的参数传递给函数. 当数组名作为实参时,传递给函数的实际上是一个指向数组起始位置的指针. /*开发环境:vs2010 window xp sp3*/ #include <stdio.h> int main(void) { void array_len(int v[]); int v[]= { 70, 30, 40

MySQL数据库知识点整理 (持续更新中)

一.修改用户密码 格式(在命令行下输入):mysqladmin -u 用户名 -p旧密码 password 新密码 1. 给root添加密码ab12:  mysqladmin -uroot -password ab12 2. 将root的密码修改为djg345:    mysqladmin -uroot -pab12 password djg345 二.添加新用户 格式:grant 权限 on 数据库名.表名 to 用户名@登录主机  identified by "密码" 1. 增加一

SQLServer中比较末尾带有空格的字符串遇到的坑(转)

http://www.cnblogs.com/happycat1988/p/4779805.html 最近发现SQLServer中比较字符串的时候 如果字符串末尾是空格 那么SQLServer会无视那些空格直接进行比较 这和程序中平时的字符串判断逻辑不统一 declare @a nvarchar(50);set @a=N'happycat1988' declare @b nvarchar(50);set @b=N'happycat1988 ' if(@a = @b) select 'True'

SQLServer中比较末尾带有空格的字符串遇到的坑

最近发现SQLServer中比较字符串的时候 如果字符串末尾是空格 那么SQLServer会无视那些空格直接进行比较 这和程序中平时的字符串判断逻辑不统一 declare @a nvarchar(50);set @a=N'happycat1988' declare @b nvarchar(50);set @b=N'happycat1988 ' if(@a = @b) select 'True' as 直接等号比较 else select 'False' as 直接等号比较 if(@a like

c编程:求出4&amp;#215;4矩阵中最大和最小元素值及其所在行下标和列下标,求出两条主对角线元素之和。

//求出4×4矩阵中最大和最小元素值及其所在行下标和列下标,求出两条主对角线元素之和 #include <stdio.h> int main() { int sum=0; int max,min; int max1,max2;//记录最大值的坐标 int min1,min2;//记录最小值的坐标 int i,j; int a[4][4]; //为数组赋值 for(i=0;i<4;i++) { for(j=0;j<4;j++) { scanf("%d",&

ubuntu 14.04中安装 ruby on rails 环境(填坑版) 呕血推荐

环境:在win7 上Vmware虚拟机环境中安装的ubuntu 14.04 开发相关: ruby 2.2.0 rails 4.2.0 sublime text 3 本文说明:所有的命令均在$ 之后,若$前边带有信息,只是为了方便你理解和与自己对照. 安装过程中由于这样那样的原因,产生许多坑,所谓坑是对初学者来说,大牛们飘过吧. 步骤1.从官网下载ubuntu 14.04 X64 http://124.205.69.136/files/2013000000502943/202.141.176.11