基本计数方法

  1. 容斥原理
  • ABC=A+B+C+BABBCCA+ABC|A∪B∪C|=|A|+|B|+|C|+|B|-|A∩B|-|B∩C|-|C∩A|+|A∩B∩C|
  1. 排列问题
  • 全排列
    Pn,k)=n(n1)(n2)...(nk+1)=n!/(nk)!P(n,k)=n(n-1)(n-2)...(n-k+1)=n!/(n-k)!
  • 组合数
    Cn,k)=P(n,k)/P(k,k)=n!/((nk)!k!)C(n,k)=P(n,k)/P(k,k)=n!/((n-k)!k!)
    • 性质 1:
      C(n,0)=C(n,n)=1C(n,0)=C(n,n)=1
    • 性质 2:
      C(n,k)=C(n,nk)C(n,k)=C(n,n-k)
    • 性质 3:
      C(n,k)+C(n,k+1)=C(n+1,k+1)C(n,k)+C(n,k+1)=C(n+1,k+1)
    • 性质 4:
      C(n,k+1)=C(n,k)(nk)/(k+1)C(n,k+1)=C(n,k)*(n-k)/(k+1)
  1. 递推关系
  • 斐波那契(Fibonacci)数列
    f(n)=f(n1)+f(n2),f(1)=f(2)=1f(n)=f(n-1)+f(n-2),f(1)=f(2)=1
  • 卡特兰(Catalan)数列
    f(n)=f(2)f(n1)+f(3)f(n2)+...+f(n1)f(2),f(2)=f(3)=1,[f(k)f(nk1)]f(n)=f(2)f(n-1)+f(3)f(n-2)+...+f(n-1)f(2),f(2)=f(3)=1,[f(k)f(n-k-1)]

数论

一. 基本概念

  1. 素数
//筛素数
const int maxn=10000000+10;
const int maxp=700000;
int vis[maxn];
int prime[maxp];
void sieve(int n){
	int m=(int)sqrt(n+0.5);
	memset(vis,0,sizeof(vis));
	for(int i=2;i<=m;i++) if(!vis[i])//0为素数
		for(int j=i*i;j<=n;j+=i) vis[j]=1;
}
//生成素数表
int gen_prime(int n){
	sieve(n);
	int c=0;
	for(int i=2;i<=n;i++) if(!vis[i])
		prime[c++]=i;
	return c;
}
  1. 欧几里得算法
typedef long long ll;
ll gcd(ll a,ll b){
	return b==0?a:gcd(b,a%b);
}
void gcd(ll a,ll b,ll &d,ll &x,ll &y){
	if(!b){d=a;x=1;y=0;}
	else{gcd(b,a%b,d,y,x);y-=x*(a/b);}
}
  1. 模算数
//返回ab mod n 要求0<=a,b<n
ll mul_mod(ll a,ll b,ll n){
	return a*b%n;
}
//返回a^p mod n,要求0<=a<n
ll pow_mod(ll a,ll p,ll n){
	if(p==0) return 1;
	ll r=pow_mod(a,p/2,n);
	r=r*r%n;
	if(p%2==1) r=r*a%n;
	return r;
}
  1. 欧拉phi函数(phi(x)表示等于不超过x且和x互素的整数个数)
    φ(x)=n(11/p1)(11/p2)...(11/pk)φ(x)=n(1-1/p1)(1-1/p2)...(1-1/pk)
//计算欧拉phi函数
int euler_phi(int n){
	int m=(int)sqrt(n+0.5);
	int ans=n;
	for(int i=2;i<=m;i++) if(n%i==0){
		ans=ans/i*(i-1);
		while(n%i==0) n/=i;
	}
	if(n>1) ans=ans/n*(n-1);
}
//用类似筛法的方法计算phi(1),phi(2),...,phi(n)
int phi[maxn];
void phi_table(int n){
	for(int i=2;i<=n;i++) phi[i]=0;
	phi[1]=1;
	for(int i=2;i<=n;i++) if(!phi[i])
		for(int j=i;j<=n;j+=i){
			if(!phi[j]) phi[j]=j;
			phi[j]=phi[j]/i*(i-1);
		}
}
  1. 剩余系
//计算模n下a的逆 如果不存在逆,返回-1
ll inv(ll a,ll n){
	ll d,x,y;
	gcd(a,n,d,x,y);
	return d==1?(x+n)%n:-1;
}

其他

  • 模拟,贪心,二分,三分,动态规划,容斥原理
  • 四舍五入:floor(pos+0.5)OR(x+s1)/sfloor(pos + 0.5) OR (x+s-1)/s
  • 错排公式:D(n)=(n1)(D(n2)+D(n1))D(n)=(n-1)(D(n-2)+D(n-1))
  • 判断凹凸多边形:p1(x1,y1),p2(x2,y2),p3(x3,y3)p1(x1,y1),p2(x2,y2),p3(x3,y3)
    s(p1,p2,p3)=(x1x3)(y2y3)(x2x3)(y1y3)s(p1,p2,p3)=(x1-x3)*(y2-y3)-(x2-x3)*(y1-y3)
    如果s>0则说明这3个点时是按照逆时针的顺序,如果是s<0则说明连接这3个点是按照顺时针的顺序。
  • 三角形面积:
    a×b=absin(θ)|a×b|=|a||b|sin(θ)
    S=1/2a×bS=1/2|a×b|
    p1(x1,y1),p2(x2,y2),p3(x3,y3)p1(x1,y1),p2(x2,y2),p3(x3,y3)
    v1(x2x1,y2y1),v2(x3x2,y3y2)v1(x2-x1,y2-y1),v2(x3-x2,y3-y2)
    v1(x1,y1),v2(x2,y2)v1(x1,y1),v2(x2,y2)
    a×b=(x1y2x2y1)a×b=(x1y2-x2y1)

模板

  1. 尺取法,滑动窗口
set<int> s;
while (r<n){
	while (r<n&&!s.count(a[r]))
		s.insert(a[r++]);
	m=max(m,r-l);
	s.erase(a[l++]);
}
  1. 归并排序,统计逆序对
exp1:
int A[maxn], T[maxn];
__int64 cnt=0,n;
void merge(int *arr,int *tmp,int l, int mid,int r){
	int i=l, j=mid+1, k=l;
	while (i<=mid&&j<=r){
		if (arr[i]<=arr[j]) tmp[k++]=arr[i++];
		else{tmp[k++]=arr[j++];cnt+=mid-i+1;}
	}
	while (i!=mid+1) tmp[k++]=arr[i++];
	while (j!=r+1) tmp[k++]=arr[j++];
	for (i=l;i<=r; i++) arr[i]=tmp[i];
}
void mergesort(int *arr, int *tmp, int l, int r){
	if (l<r){
		int mid=(l+r)/2;
		mergesort(arr,tmp,l,mid);
		mergesort(arr,tmp,mid+1,r);
		merge(arr,tmp,l,mid,r);
	}
}
exp2:
void mergesort(int *A,int *T,int x,int y){
	if(y-x>1){
		int m=(x+y)/2;
		mergesort(A,T,x,m);
		mergesort(A,T,m,y);
		int i=x,j=m,k=i;
		while(i<m||j<y){
			if(j>=y||(i<m&&a[i]<=a[j])) T[k++]=A[i++];
			else {T[k++]=A[j++];cnt=m-p;}
		}
		for(k=0;k<y;k++) A[k]=T[k];
	}
}
  1. 快速幂
int pow(int a,int b){
	int r=1,base=b;
	while(b){
		if(b&1) r*=base;
		base*=base;
		b>>=1;
	}
	return r;
}
  1. GCD/LCM
int gcd(int a,int b){
	if(a==0) return b;
	else if(b==0) return a;
	else if(a%2==0&&b%2==0) return 2*gcd(a>>1,b>>1);
	else if(a%2==0) return gcd(a>>1,b);
	else if(b&2==0) return gcd(a,b>>1);
	else return gcd(abs(a-b),min(a,b));
}
int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b){
	return a/gcd(a,b)*b;
}
  1. 分解质因数,约数个数定理
int apx(ll n){
	ll m=(int)sqrt(n+0.5);
	int sum=1;
	for (ll i=2;i<=m;++i){
		int p=0;
		while (n%i==0) {p++;n/=i;}
		if (p>0) sum*=(p+1);
	}
	if (t>1) sum*=2;
	return sum;
}
  1. 快速排序
void quicksort(int *A,int x,int y){
	if(x<y){
		int i=x,j=y,k=A[x];
		while(i<j){
			while(i<j&&A[j]>=k) j--;
			if(i<j) A[i++]=A[j];
			while(i<j&&A[i]<=k) i++;
			if(i<j) A[j--]=A[i];
			a[i]=k;
		}
		quicksort(A,x,i-1);
		quicksort(A,j+1,y);
	}
}
  1. 找最长递增子序列(LIS)
PII v[5050];
int a[5050];
//input
sort(v, v + d);
FORi(i, 0, d+1) a[i] = inf;
//找最长递增子序列(LIS)
FORi(i, 0, d) *lower_bound(a, a + d + 1, v[i].second) = v[i].second;//upper_lound为最长不下降子序列
cout << distance(a,lower_bound(a, a + d + 1, inf)) << endl;
  1. 斯特灵公式 (Stirling's approximation)

    n!2πn(ne)n.n! \approx \sqrt{2\pi n}\, \left(\frac{n}{e}\right)^{n}.

len=(n*log(n)-n+0.5*log(2*n*PI))/(log(10))+1;
  1. 回文子串 Manacher 算法
char s[110200], sn[220400];
int p[220400];
int Manacher()
{
	int len=strlen(s);
	sn[0] = '$';
	sn[1] = '#';
	int j = 2;
	for (int i = 0; i < len; i++)
	{
		sn[j++] = s[i];
		sn[j++] = '#';
	}
	sn[j] = '\0';
	int mxlen = -1, mid, mx = 0;
	for (int i = 1; i < j; i++)
	{
		p[i] = i < mx ? min(p[2 * mid - i], mx - i) : 1;
		while (sn[i - p[i]] == sn[i + p[i]]) p[i]++;
		if (mx < i + p[i]) { mid = i; mx = i + p[i]; }
		mxlen = max(mxlen, p[i] - 1);
	}
	return mxlen;
}