它就是要来了
noip数论一般会以三种形式呈现
注 码风可能有些毒 (有人说我压行qwq) 大概保持标准三十五行左右
为什么是三十五行呢 因为我喜欢这个数字 我喜欢三十五而已(足球球衣也会用这个号哒)
1.结论规律与打表技巧
这类的题最杰出的代表是小凯的疑惑
打表技巧的话主要是研究三个要点
1.一个输入数据和模数时
oeis这个时候最好用了 不过没有
我们需要重点研究的是递推关系,差,二阶差这样
这时候我们会发现三类数据
· 等差等比二阶等差二阶等比等差等比………………这种都是有通项的 考验数学能力
· 与二进制和唯一分解定理有关
这个内容多 展开说
lowbit 1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,5…………这个东西太常见了 不就是x&(-x)么 好的 可以开始考虑o n 数学方法比如化简Σ之类的
唯一分解定理目前我所见都很天然的 一眼就能看出来
二进制的1个数 这个东西是个阶梯状函数 所以也是比较容易找到规律的 至于如何统计 大概只能枚举位数
· 递推-这个时候就看一下数据 如果小或者递推关系复杂 就考虑dp与暴力枚举,否则上矩阵(一定要注意一下)
2.两到三个数据 没什么办法 直接找
3.一行数据(那其实是不等式和贪心)排序值 均值大概率在考纲之中 柯西的话我想没那么好出
本部分需要用的模板 所有代码最新手打测试无误可以使用
1.快速幂、快速乘
快速幂 :a^p-2=a^(-1)(modp);
1 #include<iostream> 2 #include<cmath> 3 #include<algorithm> 4 #define ll long long 5 using namespace std; 6 ll n,k,x,a,b; 7 ll ksc(ll a,ll b,ll p){ll ans=0,base=a; 8 for(;b;b>>=1){if(b&1)ans+=base,ans%=p; 9 base*=2,base%p; 10 }return ans; 11 } 12 int main(){cin>>a>>b>>n; 13 cout<<ksc(a,b,n)<<endl; 14 }
#include<iostream> #include<cmath> #include<algorithm> #define ll long long using namespace std; ll n,k,x,a,b; ll ksm(ll a,ll x,ll p){ll ans=1,base=a; for(;x;x>>=1){if(x&1)ans*=base,ans%=p; base*=base,base%p; }return ans; } int main(){cin>>a>>b>>n; cout<<ksm(a,b,n)<<endl; }
2.线性筛素数 phi mu
1 #include<iostream> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 #define ll long long 6 #define maxn 5000001 7 bool isprime[maxn];int n,m,sum=0,prime[maxn]; 8 void shai(){ 9 for(int i=2;i<maxn;i++){ 10 if(!isprime[i])prime[sum++]=i; 11 for(int j=0;j<sum&&i*prime[j]<maxn;j++){ 12 isprime[i*prime[j]]=1;if(i%prime[j]==0)break; 13 } 14 } 15 }int main(){ 16 cin>>n;shai();for(int i=0;i<=n;i++)cout<<prime[i]<<" "; 17 }
1 #include<iostream> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 #define ll long long 6 #define maxn 5000001 7 bool isprime[maxn];int n,m,sum=0,prime[maxn],phi[maxn]; 8 void shai(){phi[1]=1,phi[2]=1; 9 for(int i=2;i<maxn;i++){ 10 if(!isprime[i])prime[sum++]=i,phi[i]=i-1; 11 for(int j=0;j<sum&&i*prime[j]<maxn;j++){ 12 isprime[i*prime[j]]=1;phi[i*prime[j]]=phi[i]*(prime[j]-1); 13 if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];break;} 14 } 15 } 16 }int main(){ 17 cin>>n;shai();for(int i=0;i<=n;i++)cout<<phi[i]<<" "; 18 }
1 #include<iostream> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 #define ll long long 6 #define maxn 5000001 7 bool isprime[maxn];int n,m,sum=0,prime[maxn],phi[maxn],mu[maxn]; 8 void shai(){phi[1]=1,phi[2]=1;mu[1]=1; 9 for(int i=2;i<maxn;i++){ 10 if(!isprime[i])prime[sum++]=i,phi[i]=i-1,mu[i]=-1; 11 for(int j=0;j<sum&&i*prime[j]<maxn;j++){ 12 isprime[i*prime[j]]=1;phi[i*prime[j]]=phi[i]*(prime[j]-1);mu[i*prime[j]]=-mu[i]; 13 if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j],mu[i*prime[j]]=0;break;} 14 } 15 } 16 }int main(){ 17 cin>>n;shai();for(int i=0;i<=n;i++)cout<<mu[i]<<" "; 18 }
3.数论分块 这是个技巧……
4.矩阵快速幂
这个有点烦的是一直dev调试这个会出大问题 好像就是dev5.4.2的bug一样
他在调试时会自己死机 一会又好了 有时候烦的一批 鬼使一样
并且奇特的是新建矩阵居然不会自动清空这就很奇特
可能是个大bug 5.4.2的版本会出这个问题 考试时候可能是5.4.2 所以说解决办法就只有一个 一遍打对,每次新建memset
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define maxn 105 5 #define mod 1000000007 6 struct matrix{ll a[maxn][maxn],len,row;}m1;ll n,m,k,l; 7 matrix jc(matrix a,matrix b){ 8 matrix now;now.len=b.len,now.row=a.row;memset(now.a,0,sizeof(now.a)); 9 for(int i=1;i<=a.row;i++){ 10 for(int j=1;j<=b.len;j++){ 11 for(int k=1;k<=a.len;k++){ 12 now.a[i][j]+=a.a[i][k]*b.a[k][j],now.a[i][j]%=mod; 13 } 14 } 15 }return now; 16 }matrix ksm(matrix a,ll x){ 17 matrix base=a,ans;ans.len=a.len,ans.row=a.row;memset(ans.a,0,sizeof(ans.a)); 18 for(int i=1;i<=a.len;i++)ans.a[i][i]=1; 19 for(;x;x>>=1){ 20 if(x&1)ans=jc(ans,base); 21 base=jc(base,base); 22 }return ans; 23 24 } 25 int main(){cin>>n>>k;m1.len=m1.row=n; 26 for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)cin>>m1.a[i][j]; 27 m1=ksm(m1,k); 28 for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)cout<<m1.a[i][j]<<" "; 29 cout<<endl;}return 0; 30 }
5.二进制 这也是技巧 没有代码
这一类题大概也就这样 真的出来了什么鬼题也没办法
2.正宗数论
其实有意思的一匹 很有意思
1.带余除法为核心
·逆元线性递推
设 t=ki+b;求t^(-1)
图片出处-guessycb暗中给了我许多 十分感谢
1 #include<iostream> 2 using namespace std; 3 #define maxn 5000005 4 #define ll long long 5 ll n,m,p,f[maxn]; 6 int main(){cin>>n>>p; 7 f[1]=1;for(int i=2;i<=n;i++){ 8 f[i]=(p-p/i)*f[p%i]%p; 9 }for(int i=1;i<=n;i++)printf("%lld\n",f[i]); 10 }
2.唯一分解定理为核心 这个都知道就是什么因子和因子个数 有的时候你以为很暴力 不过由于那个基本时间复杂度定理 它是跟n的或者log的或者on的
3.exgcd为核心
这个大概有两类出法 1.青蛙的约会——荒野猎人 这两个基本是必做题吧 ab可不互质啊
求同余方程解 a=b(modc)->ax+cy=b; (a,c)=d;
有ax0+cy0=1 x=(x0*(b/d)+p)%p*c 此处p为x取值周期
第二类 关于gcd、lcm 那个乘积代换式一定不会单独考 因为太low
gcd这个物质它不但可以与exgcd建立莫大的联系 还可以维系phi mu 莫比乌斯反演 那种东西其实很套路 有趣的一匹
跟exgcd的时候 我们熟知 m(a,b)=(ma,mb)=m(a+b,b)这样就是他 还真有这么几道题
4.crt 一张图
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define maxn 1000005 5 ll n,m[maxn],l,a[maxn],t[maxn],M=1,k[maxn],ans=0; 6 void exgcd(ll a,ll b,ll &x,ll &y){ 7 if(!b){x=1,y=0;return ;} 8 exgcd(b,a%b,y,x);y-=a/b*x; 9 } 10 ll qpow(ll a,ll x,ll p){ll ans=1,base=a; 11 for(;x;x>>=1){ 12 if(x&1)ans=ans*base%p; 13 base=base*base%p; 14 }return ans; 15 } 16 int main(){ 17 cin>>n; 18 for(int i=1;i<=n;i++)cin>>a[i]>>m[i],M*=m[i]; 19 for(int i=1;i<=n;i++){t[i]=M/m[i];exgcd(t[i],m[i],k[i],l); 20 k[i]=(k[i]%m[i]+m[i])%m[i]; 21 ans=(ans+a[i]*t[i]*k[i])%M; 22 }cout<<ans<<endl; 23 }
5.卢卡斯 它的本质是模数意义下的c 配合crt求出原始值(古代猪文)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 using namespace std; 6 #define ll long long 7 #define maxn 300005 8 ll f[maxn],inv[maxn],n,m,k,p; 9 ll qpow(ll a,ll x,ll p){ll ans=1,base=a; 10 for(;x;x>>=1){ 11 if(x&1)ans=ans*base%p;base=base*base%p; 12 }return ans; 13 } 14 ll c(ll n,ll m,ll p){if(m<n)return 0; 15 ll a=qpow(f[n],p-2,p),b=qpow(f[m-n],p-2,p); 16 return f[m]*a*b%p; 17 } 18 ll lucas(ll n,ll m,ll p){ 19 if(!n)return 1; 20 return c(n%p,m%p,p)*lucas(n/p,m/p,p)%p; 21 } 22 int main(){cin>>k; 23 while(k--){ 24 cin>>n>>m>>p,f[0]=1; 25 for(int i=1;i<maxn;i++)f[i]=f[i-1]*i%p; 26 cout<<lucas(n,n+m,p)<<endl; 27 } 28 }
6.欧拉定理和马小
有个题叫a^b^c%p
这个东西怎么求呢 就是欧拉定理 a^b=a^((b%phi[p])+phi[p])(modp)但是需要特判
(1)当n>1,(a,n)=1时, a^b%n=a^b%φ(n)%n
(2)当b≤φ(n)时,直接计算即可。
(3)当b>φ(n)时 , 刚才的式子
7.phi的用法
几个零碎的点
·观察者问题
·互质对 这个说一下 求Σ(1-n)Σ(1-m)【gcd(i,j)==1】是那个莫反套路
刻意背过也可以 ∑ i=1-n∑ j=1-m [gcd(i,j)=1] =∑ i=1-n μ(i) ⌊n/i⌋∗⌊m/i⌋
如果把第二个m变成n就大不一样了
对于1每个n(1-n)与他互质且比他小(避免算重)的个数就是phi【i】
·母函数与二项式定理
考过二项式定理裸题 母函数的话主要是求 一个奇怪的组合数 然后对函数加减
不过noip不太会考它
3.几何和组合
数学的话还要看一手几何和组合数学
有点相同 所以一起看一手
组合——几个方法
·隔板法 要求划分出非空k集 =c(k,n+1)有空集一样 等于每个集合多个元素罢了
·折线法 其实是组合和坐标的加和
(1,1)-》(n,m)方案=c(n,n+m);
然后 我们考虑一种到达目标点且不经过固定几何图形的方案数
其实就是容斥,目标点关于几何图形对称 方案数就是相减
·高级容斥 想清楚要求什么 什么好求
几何
·矢量叉积-求面积 夹角
·矢量旋转
已知任意一个平面向量ab=(x,y) ,把向量ab绕其起点沿逆时针方向旋转a角得到向量AP=(xcosa-ysina,xsina+ycosa)
一堆操作 在这个板子里 还有就是旋转九十度什么的就直接搞点就行 向量用不着
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n;double ans,m; 4 struct node{double x,y;}a[1000005]; 5 node add(node a,node b){node c;c.x=a.x+b.x,c.y=a.y+b.y;return c;}//+ 6 node niadd(node a,node b){node c;c.x=a.x-b.x,c.y=a.y-b.y;return c;}//- 7 8 double dot(node a,node b){return a.x*b.x+a.y*b.y;}//· 9 double cha(node a,node b){return a.x*b.y-a.y*b.x;}//* 10 11 double len(node a){return sqrt(a.x*a.x+a.y*a.y);}// |a| 12 double squ(){ // S 13 for(int i=1;i<n;i++)ans+=(double)cha(a[i],a[i+1])/2; 14 ans+=(double)cha(a[n],a[1])/2; 15 return fabs(ans); 16 } 17 double geta(node a,node b){//获得小夹角cos值 18 return dot(a,b)/(len(a)*len(b)); 19 } 20 node rotate(node a,double coss){//旋转arccosa度(正向)(逆时针) 21 double sins=sqrt(1-coss*coss); 22 node c;c.x=a.x*coss-a.y*sins,c.y=a.x*sins+a.y*coss; 23 return c; 24 } 25 double angle(double cosa){ 26 return acos(cosa); 27 } 28 29 node adjust(node a,double b){ 30 a.x*=b/len(a),a.y*=b/len(a);return a; 31 } 32 int main(){ 33 cin>>n>>m;for(int i=1;i<=n;i++)cin>>a[i].x>>a[i].y; 34 printf("%.4f\n",squ()*(double)m); 35 }
·凸包
这个真的捞 因为手打队列具有无比的优势 就是他需要访问栈顶第二个元素 求出上凸壳然后怎么办呢 noip的话大概二分y轴会有机会考吧
还有就是凸包可以做直线的相交问题
尤其是相交+极值。
上几个板子
1.洛谷模板 叉积判断夹角正负
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cmath> 5 using namespace std; 6 struct node{double x,y;}p[10005],s[10005]; 7 int n;double ans,mid; 8 double CJ(node a1,node a2,node b1,node b2){ 9 return (a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y); 10 } 11 double dis(node p1,node p2){ 12 return sqrt( (double)(p2.y-p1.y)*(p2.y-p1.y)*1.0+(double)(p2.x-p1.x)*(p2.x-p1.x)*1.0 ); 13 } 14 bool cmp(node p1,node p2){ 15 double tmp=CJ(p[1],p1,p[1],p2);if(tmp>0) return 1; 16 if(tmp==0 && dis(p[0],p1)<dis(p[0],p2)) return 1; 17 return 0; 18 } 19 20 int main(){scanf("%d",&n); 21 for(int i=1;i<=n;++i){ 22 scanf("%lf%lf",&p[i].x,&p[i].y); 23 if(i!=1&&p[i].y<p[1].y){ 24 mid=p[1].y;p[1].y=p[i].y;p[i].y=mid; 25 mid=p[1].x;p[1].x=p[i].x;p[i].x=mid; 26 } 27 } 28 sort(p+2,p+1+n,cmp);s[1]=p[1];int tot=1; 29 for(int i=2;i<=n;i++){ 30 while(tot>1&&CJ(s[tot-1],s[tot],s[tot],p[i])<=0) tot--; 31 tot++;s[tot]=p[i]; 32 }s[tot+1]=p[1]; 33 for(int i=1;i<=tot;i++) ans+=dis(s[i],s[i+1]); 34 printf("%.2lf\n",ans);return 0; 35 }
2.板子中的板子[HNOI2008]水平可见直线
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define maxn 50005 4 #define ll long long 5 ll n,m,k,a,b,c,s[maxn],top=0,ans[maxn]; 6 struct node{int k,b,id;}l[maxn]; 7 bool cmp(node a,node b){ 8 return a.k==b.k?a.b>b.b:a.k<b.k; 9 }double getx(int a,int b){ 10 return (double)(l[a].b-l[b].b)/(double)(l[a].k-l[b].k); 11 } 12 int main(){ 13 cin>>n;for(int i=1;i<=n;i++)cin>>l[i].k>>l[i].b,l[i].id=i; 14 sort(l+1,l+n+1,cmp); 15 for(int i=1;i<=n;i++){ 16 if(l[i].k==l[i-1].k&&i!=1)continue; 17 while(top>1&&getx(s[top],i)>=getx(s[top],s[top-1]))top--; 18 s[++top]=i; 19 ans[top]=l[i].id; 20 }sort(ans,ans+top+1); 21 for(int i=1;i<=top;i++)printf("%d ",ans[i]); 22 }
·三分
noip很可能在考纲里因为它就是二分的翻版。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 ll n,m,k;double a[30],l,r; 5 double f(double pos){double ans=0; 6 for(int i=1;i<=n+1;i++){ 7 ans+=pow(pos,n-i+1)*a[i]; 8 }return ans; 9 } 10 double sanfen(double l,double r){ 11 if(r-l<0.000001)return l; 12 double mid1=l+(r-l)/3,mid2=r-(r-l)/3; 13 if(f(mid1)>f(mid2))return sanfen(l,mid2); 14 else return sanfen(mid1,r); 15 } 16 int main(){ 17 cin>>n>>l>>r; 18 for(int i=1;i<=n+1;i++)cin>>a[i]; 19 printf("%.5f",sanfen(l,r)); 20 }
4.其他
细数了一下其实剩下的只有俩——高斯消元与行列式和线型基 要是考什么bsgs 就很烦了 不过bsgs还是看一看最好
另外什么牛顿迭代 matrixtree之类的谁知道他会不会灵性来一下 都发下
高消(行列式矩阵树都是对对角线矩阵求积)构造矩阵自己查吧 这个好像不属于板子(逃)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cstdlib> 6 #include<cmath> 7 #include<vecotr> 8 #define maxn 100 9 using namespace std; 10 int n,m,k,l,a,b,c; 11 double mp[maxn][maxn]; 12 int main(){ 13 cin >> n;for(int i=1;i<=n;i++) 14 for(int j=1;j<=n+1;j++) 15 cin>>mp[i][j]; 16 for(int j=1;j<=n;j++){ 17 int rgt=0; 18 for(int i=j;i<=n;i++) 19 if(mp[i][j]){rgt=i;break;} 20 if(!rgt)continue; 21 if(rgt^j)swap(mp[rgt],mp[j]); 22 23 for(int i=j+1;i<=n;i++){ 24 double div=mp[i][j]/mp[j][j]; 25 for(int k=1;k<=n+1;k++)mp[i][k]-=div*mp[j][k]; 26 } 27 } 28 for(int j = n; j >= 1; j --){ 29 if(mp[j][j] == 0){cout<<"No Solution"; return 0;} 30 mp[j][n+1] = mp[j][n+1] / mp[j][j]; 31 for(int i = j-1; i >= 1; i --)mp[i][n+1] -= mp[j][n+1] * mp[i][j]; 32 } 33 for(int i = 1; i <= n; i ++)printf("%.2lf\n" ,mp[i][n+1]); 34 return 0; 35 }
牛顿迭代求高次开跟
1 #include<bits/stdc++.h> 2 #define first 233.0 3 #define ll long long 4 #define ld double 5 ld n,m,k,l,a,b,c,x; 6 using namespace std; 7 ll ksm(ll a,ll x){ll base=a,ans=1; 8 for(;x;x>>=1){ 9 if(x&1)ans=base*ans; 10 base=base*base; 11 } 12 return ans; 13 } 14 int main(){ 15 cin>>a>>m;x=first; 16 for(int i=1;i<=100;i++)x=x-x/m+a/((ld)1*m*ksm(x,m-1)); 17 cout<<x<<endl; 18 }
线型基
这个东西只有一种用处 求最大异或和
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define maxn 105 5 ll n,m,k=0,l,s[maxn],a[maxn]; 6 void insert(ll x){ 7 for(int i=50;i>=0;i--){if(!x)return ; 8 if((x&(1ll<<i))!=0){ 9 if(!s[i])s[i]=x; 10 x^=s[i]; 11 } 12 } 13 } 14 int main(){ 15 cin>>n;for(int i=1;i<=n;i++)cin>>a[i],insert(a[i]); 16 for(int i=50;i>=0;i--)if((k^s[i])>k)k^=s[i]; 17 cout<<k<<endl;return 0; 18 }
那么数论基本结束了
高斯消元是薄弱点 第二次争取全对(其实有错更好)
祝rp++ 高分预定!!!