模板
数学
•素数筛
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 文件即可开始快乐的对拍之旅;