模板

 

 

数学

•素数筛

 1 ///欧拉筛法(时间复杂度O(n),n为需要筛的最大的数)
 2 ///[1~maxVal]最多有不超过maxCnt个素数
 3 ///maxVal:筛出[1~maxVal]中的素数
 4 int prime[maxCnt];///素数表,存储[1~maxVal]中的所有素数
 5 bool isPrime[maxVal];///isPrime[i]:判断i是否为素数
 6 void Prime()
 7 {
 8     mem(isPrime,true);
 9     isPrime[1]=false;
10     
11     int cnt=0;
12     for(int i=2;i < maxVal;++i)
13     {
14         if(isPrime[i])///判断i是否为素数
15             prime[++cnt]=i;///如果i为素数,加入素数表
16         ///赛区由已有的素数确定的合数
17         for(int j=1;j <= cnt && prime[j]*i < maxVal;++j)
18         {
19             ///筛去素数prime[j]的i倍
20             isPrime[i*prime[j]]=false;
21             if(i%prime[j] == 0)///优化的关键语句,确保每个合数只被一个素数筛去
22                 break;
23         }
24     }
25 }

疑惑:

  如何确保任意合数只被筛去一次?

  例如:12 = 2*6 = 3*4

  怎样才能确保12只被 2*6 筛去,而不被 3*4 筛去?

  确保的关键就是

    if( I % prime[j] == 0)

      break;

对欧拉筛法优化语句的理解:

 1 引理1:
 2     假设 x*y = a*b,其中 x,a 为质数,那么有 x | b , a | y.
 3 例如:
 4     2*6 = 3*4
 5     2 | 4 , 3 | 6
 6 引理2:
 7     任何一个合数都可以表示成一个质数和一个整数的乘积。
 8 
 9 对于一个合数 A
10 ①如果 A 只能分解成一个 素数×整数 的形式,那么这个整数必为素  数,这也就意味着 i 为素数;
11    那么语句 if( i%prime[j] == 0) 始终为false.
12    例如:假设A=21=3*7
13    当 i = 7 是,会与之前求出的所有的素数相乘,使得 isPirme[ 7×3 ] =false
14    那么对于所有的只能分解成 素数×素数 形式的合数会全部筛去。
15 
16 ②如果 A 可以分解成多个 素数×整数 的形式
17    不妨假设 A = a1*b1 = a2*b2 ,并且 a1,a2为素数,a1 < a2;
18    那么由引理1可得
19         a1 | b2 , a2 | b1
20   由于 a1 < a2,所以 b1 > b2,
21   当 i = b2时,在执行完 isPrime[ a1*b2 ]=false 后,一定会执行 if( b2%a1 == 0) 这条语句,
22   那么就不会执行 isPrime[ a2*b2 ]=false 这条语句。
23   所以,这句话确保了每个合数只被筛去一次。
24 
25 另一种理解:
26     如果 i%prime[j] = 0;
27     那么,不妨令 i = prime[j]×q;
28     对于比 prime[j] 大的素数 prime[k]:
29         i×prime[k] = prime[j]×q×prime[k];
30     完全可以在 i' = q×prime[k] 时通过prime[j] 筛去prime[k]×i;
对优化语句的深入理解

•快速幂

 1O(log(b))求解a^b%mod
 2 ll qPow(ll a,ll b,ll mod)///a^b%mod
 3 {
 4     ll ans=1;
 5     a %= mod;
 6     while(b)
 7     {
 8         if(b&1)
 9             ans=ans*a%mod;
10  
11         a=a*a%mod;
12         b >>= 1;
13     }
14     return ans;
15 }

详解:戳这里👉

•逆元

  给定整数 a,且 GCD(a,p) = 1,则称 ax ≡ 1(mod p) 的一个整数解为 a 模 p 的逆;

  x 就称为 a 模 p 的逆元;

  求解 x 的方法;

  1) m 为素数

    根据费马小定理:ap-1 ≡ 1(mod p);

    即 a·ap-2 ≡ 1(mod p);

    a 模 p 的逆元就为 ap-2;

    求解方法:

1 小范围[1,1000000]内的数求解可以考虑用数组
2 inv[1]=1;
3 for(int i=2;i < maxn;i++)
4     inv[i]=MOD-(MOD/i)*inv[MOD%i]%MOD;
 1 如果 a 为比较大的数,用快速幂求解逆元
 2 ll qPow(ll a,ll b)
 3 {
 4     ll ans=1;
 5     a %= MOD;
 6 
 7     while(b)
 8     {
 9         if(b&1)
10             ans=ans*a%MOD;
11 
12         a=a*a%MOD;
13         b >>= 1;
14     }
15     return ans;
16 }
17 ll Inv(ll a)///返回 a 的逆元
18 {
19     return qPow(a,MOD-2)%MOD;
20 }

 

 


 

其他 

•随机数生成器

参考资料:[1]:PPT(提取码:2xiv)

1.小范围随机数生成器:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 double random(double a,double b)///随机数产生器,其范围为[a,b);
 5 {
 6     return a+(b-a)*rand()/(RAND_MAX+1.0);
 7 }
 8 int main()
 9 {
10     srand(unsigned(time(0)));///用来设置rand()产生随机数时的随机数种子
11 
12     for(int i=1;i <= 10;++i)///随机产生10个数
13         cout<<(int)random(1,5)<<endl;
14 }

2.超大范围随机数生成器

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ul unsigned long
 4 const int MAX=1e6;
 5 const int MIN=1;
 6 const int maxn=2e3+50;
 7 
 8 int n;
 9 
10 ul ulrand()
11 {
12     return (((ul)rand()<<24)&0xFF000000ul) |
13            (((ul)rand()<<12)&0x00FFF000ul) |
14            (((ul)rand())&0x00000FFFul);
15 }
16 int main()
17 {
18     ///生成[MIN,MAX]之间的一个随机数
19     int bigRand=ulrand()%(MAX-MIN+1)+MIN;
20 }

•使用 bat 对拍

1.参考资料:

  [1]:ACM/OI 对拍程序的写法

2.具体方法

  新建一个文件夹,命名为 【对拍】;

  在这个文件夹中新建 4 个文件,分别为:

    对拍.bat , rand.cpp , my.cpp , std.cpp;

  

  打开 对拍.bat,并在里面加入如下代码:

1 @echo off  
2 :loop
3     rand.exe > bug.in
4     my.exe < bug.in > my.out
5     std.exe < bug.in > std.out
6     fc my.out std.out
7 if not errorlevel 1 goto loop  
8 pause

  打开 rand.cpp,并在里面加入如下代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ul unsigned long
 4 
 5 int random(int a,int b)///随机生成[a,b]之间的小随机数
 6 {
 7     return a+(b-a+1)*rand()/(RAND_MAX+1.0);
 8 }
 9 ul ulrand(int a,int b)///随机生成[a,b]之间的超大随机数
10 {
11     return ((((ul)rand()<<24)&0xFF000000ul) |
12            (((ul)rand()<<12)&0x00FFF000ul) |
13            (((ul)rand())&0x00000FFFul))%(b-a+1)+a;
14 }
15 
16 stringstream ss;
17 int main(int argc,char *argv[])
18 {
19     int seed=time(NULL);
20     if(argc > 1)///如果有参数
21     {
22         ss.clear();
23         ss<<argv[1];
24         ss>>seed;///把参数转换成整数赋值给seed
25     }
26     srand(seed);///用来设置rand()产生随机数时的随机数种子
27     /*****上述代码照抄,目的是加快随机数的产生******/
28     
29     /**
30         以下代码为所需要的随机数据
31         如果需要T组数据,令T=1
32     */
33 }

  my.cpp 中放入待调bug代码;

  std.cpp 中放入标程或暴力代码;

  运行 rand.cpp , my.cpp , std.cpp 生成相应的 .exe 文件;

  之后,双击 对拍.bat 文件即可开始快乐的对拍之旅;

 

posted @ 2019-07-17 16:47  HHHyacinth  阅读(193)  评论(0编辑  收藏  举报