1. 程式人生 > >NOIP複習提綱(持續更新)

NOIP複習提綱(持續更新)

1.LCA 預處理(O(nlogn)) 單次查詢O(logn)
NKOJ2447

#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
int n,s,m;
int End[10010],Last[10010],Next[10010];
int deep[10010];
int f[10010][20];
int du[10010];
void deal_first(int u,int father)
{
	deep[u]=deep[father]+1;
	for(int i=0;i<=s;i++)
	f[u][i+1]=f[f[u][i]][i];
	for(int i=Last[u];i;i=Next[i])
	{
		f[End[i]][0]=u;
		deal_first(End[i],u);
	}
}
int LCA(int x,int y)
{
	if(deep[x]<deep[y]) swap(x,y);
	for(int i=s;i>=0;i--)
	{
		if(deep[f[x][i]]>=deep[y])
		x=f[x][i];
		if(x==y) return x;
		if(deep[x]==deep[y]) break;
	}
	for(int i=s;i>=0;i--)
	{
		if(f[x][i]!=f[y][i])
		{
			x=f[x][i];
			y=f[y][i];
		}
	}
	return f[x][0];
	
}
int main()
{
	int a,b,x,y;
	scanf("%d",&n);
	s=ceil(log2(n));
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		End[i]=y;
		Next[i]=Last[x];
		Last[x]=i;
		du[y]++;
	}
	for(int i=1;i<=n;i++)
	{
		if(!du[i])
		{
			deal_first(i,0);
			break;
		}
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a,&b);
		printf("%d\n",LCA(a,b));
	}
	return 0;
}

2.ST表 預處理(nlogn) 單次詢問O(1)
luogu3865

#include<cstdio>
#include<iostream>
using namespace std;
int n,q,a[100010],p,f[100010][20];
int log[100010];
int main()
{
    scanf("%d%d",&n,&q);
    log[0]=-1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        f[i][0]=a[i];
        log[i]=log[i>>1]+1;
    }
    for(int i=1;i<=log[n];i++)
    {
        for(int j=1;j+(1<<i)-1<=n;j++)
        {
            f[j][i]=max(f[j][i-1],f[j+(1<<i-1)][i-1]);
        }
    }
    for(int i=1;i<=q;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        p=log[y-x+1];
        printf("%d\n",max(f[x][p],f[y-(1<<p)+1][p]));
    }
    return 0;
}

3.樹狀陣列 單次修改O(logn) 單次查詢O(logn)
luogu3374

#include<cstdio>
using namespace std;
int n,m;
int a[500010];
int c[500010];
int lowbit(int x)
{
	return x&(-x);
}
void modify(int x,int y)
{
	for(;x<=n;x+=lowbit(x)) c[x]+=y;
}
int getsum(int x)
{
	int ans=0;
	for(;x>=1;x-=lowbit(x)) ans+=c[x];
	return ans;
}
int main()
{
	int k,x,y;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]),modify(i,a[i]);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&k,&x,&y);
		if(k==1)
		modify(x,y);
		else
		printf("%d\n",getsum(y)-getsum(x-1));
	}
	return 0;
}

4.線段樹
(1).單點修改,區間查詢 單次修改O(logn) 單次查詢O(logn)
LOJ130

#include<cstdio>
#define ll long long
using namespace std;
ll sum[4000010];
int n,q;
ll a[1000010];
void biuldtree(int p,int l,int r)
{
	if(l==r)
	{
		sum[p]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	biuldtree(p<<1,l,mid);
	biuldtree(p<<1|1,mid+1,r);
	sum[p]=sum[p<<1]+sum[p<<1|1];
}
void change(int p,int l,int r,int x,int y)
{
	if(l==r&&l==x)
	{
		sum[p]+=(ll)y;
		return;
	}
	if(l>x||r<x) return;
	int mid=(l+r)>>1;
	change(p<<1,l,mid,x,y);
	change(p<<1|1,mid+1,r,x,y);
	sum[p]=sum[p<<1]+sum[p<<1|1];
}
ll Ask(int p,int l,int r,int x,int y)
{
	if(x>r||y<l) return 0;
	if(x<=l&&y>=r) return sum[p];
	int mid=(l+r)>>1;
	ll lsum,rsum;
	lsum=Ask(p<<1,l,mid,x,y);
	rsum=Ask(p<<1|1,mid+1,r,x,y);
	return lsum+rsum;
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
	biuldtree(1,1,n);
	int k,x,y;
	while(q--)
	{
		scanf("%d%d%d",&k,&x,&y);
		if(k==1)
		{
			change(1,1,n,x,y);
		}
		else
		{
			printf("%lld\n",Ask(1,1,n,x,y));
		}
	}
	return 0;
}

(2).區間修改,區間查詢 單次修改O(logn) 單次查詢O(logn)
LOJ132

#include<cstdio>
#define ll long long
using namespace std;
int n,q;
ll a[1000010];
ll Lazy[4000010],sum[4000010];
void biuldtree(int p,int l,int r)
{
    if(l==r)
    {
        sum[p]=a[l];
        return;
    }
    int mid=(l+r)>>1;
    biuldtree(p<<1,l,mid);
    biuldtree(p<<1|1,mid+1,r);
    sum[p]=sum[p<<1]+sum[p<<1|1];
}
void pushdown(int p,int l,int r)
{
    int mid=(l+r)>>1;
    Lazy[p<<1]+=Lazy[p];
    sum[p<<1]+=(ll)(mid-l+1)*Lazy[p];
    Lazy[p<<1|1]+=Lazy[p];
    sum[p<<1|1]+=(ll)(r-mid)*Lazy[p];
    Lazy[p]=0;
}
void change(int p,int l,int r,int x,int y,int v)
{
    if(x>r||y<l) return;
    if(x<=l&&y>=r)
    {
        Lazy[p]+=(ll)v;
        sum[p]+=(ll)(r-l+1)*v;
        return;
    }
    if(Lazy[p]) pushdown(p,l,r);
    int mid=(l+r)>>1;
    change(p<<1,l,mid,x,y,v);
    change(p<<1|1,mid+1,r,x,y,v);
    sum[p]=sum[p<<1]+sum[p<<1|1];
}
ll Ask(int p,int l,int r,int x,int y)
{
    if(x>r||y<l) return 0;
    if(x<=l&&y>=r)
    {
        return sum[p];
    }
    if(Lazy[p]) pushdown(p,l,r);
    int mid=(l+r)>>1;
    return Ask(p<<1,l,mid,x,y)+Ask(p<<1|1,mid+1,r,x,y);
}
int main()
{
    int k,l,r,x;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
    }
    biuldtree(1,1,n);
    while(q--)
    {
        scanf("%d%d%d",&k,&l,&r);
        if(k==1)
        {
            scanf("%d",&x);
            change(1,1,n,l,r,x);
        }
        else
        {
            printf("%lld\n",Ask(1,1,n,l,r));
        }
    }
    return 0;
}

5.並查集 O(近似常數)
luogu3367

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
int father[200001];
int find(int x)
{
    if(father[x]==x) return x;
    return father[x]=find(father[x]);
}
void hebing(int x,int y)
{
    father[y]=x;
}
int main()
{
    int n,m,i,z,x,y,z1,z2;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++) father[i]=i;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&z,&x,&y);
        if(z==1)
        {
            z1=find(x);
            z2=find(y);
            if(z1!=z2)
            {
                hebing(z1,z2);
            }
        }
        else
        {
            if(find(x)==find(y)) printf("Y\n");
            else printf("N\n");
        }
    }
    return 0;
}

6.迪傑斯特拉(邊不能有負權) O(mlogn)
NKOJ3639

#include<stdio.h>//這個在比賽中不加,這道題不加好像要被卡
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
using namespace std;
struct Node
{
	int Num;
	ll dis;
	bool operator<(const Node &a) const
	{
		return a.dis<dis;
	}
};
priority_queue<Node> q;
int n,m;
ll dis[400010],Len[2000010];
int End[2000010],Next[2000010],Last[400010];
bool mark[400010];
void Dij(int s)
{
	int v,u;
	Node temp;
	temp.Num=s;
	temp.dis=0;
	q.push(temp);
	while(!q.empty())
	{
		u=q.top().Num;
		q.pop();
		if(mark[u]) continue;
		mark[u]=1;
		for(int i=Last[u];i;i=Next[i])
		{
			v=End[i];
			if(!mark[v]&&dis[v]>dis[u]+Len[i])	
			{
				dis[v]=dis[u]+Len[i];
				temp.Num=v,temp.dis=dis[v];
				q.push(temp);
			}
		}
	}
}
int main()
{
	int x,y;
	ll z;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%lld",&x,&y,&z);
		End[i]=y;
		Len[i]=z;
		Next[i]=Last[x];
		Last[x]=i;
	}
	scanf("%d%d",&x,&y);
	memset(dis,0x3f,sizeof(dis));
	dis[x]=0;
	Dij(x);
	printf("%lld",dis[y]);
	return 0;
}

7.Floyd(不能有負權迴路) O(n^3)
hdoj1599

#include<bits/stdc++.h>
using namespace std;
int n,m;
int dis[101][101];
int Map[101][101];
int ans;
void floyd()
{
	int i,j,k;
	for(k=1;k<=n;k++)
	{
		for(i=1;i<k;i++)
		for(j=i+1;j<k;j++)
		ans=min(ans,dis[i][j]+Map[i][k]+Map[k][j]);//求最小環
		for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
		dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
	}
}
int main()
{
	while(scanf("%d%d",&n,&m)==2)
	{
		ans=10000000+100;
		int i;
	    for(int i=1;i<=n;i++)
	    for(int j=1;j<=n;j++)
	    Map[i][j]=dis[i][j]=10000000+100;
	    for(i=1;i<=n;i++)
	    Map[i][i]=dis[i][i]=0;
	    for(i=1;i<=m;i++)
	    {
		    int a,b,c;
		    scanf("%d%d%d",&a,&b,&c);
		    Map[a][b]=Map[b][a]=dis[a][b]=dis[b][a]=min(Map[a][b],c);
	    }
	    floyd();
	    if(ans<10000000+100) printf("%d\n",ans);
	    else printf("It's impossible.\n");
	}
	return 0;
}

8.spfa
時間複雜度O(kE)
k指每個點的平均進隊次數,一般為2
E指邊的總數,所以期望時間複雜度為O(2E)
可用於負權
可判斷負權迴路:每個點的進隊次數不超過N
資訊學奧賽一本通1382

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=500010<<1;
queue<int> q;
int n,m;
int End[N],Last[N],Next[N],Len[N];
int dis[N];
bool mark[N];
//int cnt[N];
void spfa()
{
	int u,v; 
	mark[1]=1;
	q.push(1);
	while(!q.empty())
	{
		u=q.front();
		q.pop();
		mark[u]=0;
		for(int i=Last[u];i;i=Next[i])
		{
			v=End[i];
			if(dis[v]>dis[u]+Len[i])
			{
				dis[v]=dis[u]+Len[i];
				if(!mark[v])
				{
					mark[v]=1;
					q.push(v);
					/*cnt[v]++;
					if(cnt[v]==n)
					{
						cout<<"有負環";
						exit(0);
					}*/
				}
			}
		}
	}
}
int main()
{
	int a,b,c;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		End[i]=b;
		Len[i]=c;
		Next[i]=Last[a];
		Last[a]=i;
		End[i+m]=a;
		Len[i+m]=c;
		Next[i+m]=Last[b];
		Last[b]=i+m;
	}
	memset(dis,0x3f,sizeof(dis));
	dis[1]=0;
	//cnt[1]=1;
	spfa();
	printf("%d",dis[n]);
	return 0;
}

9.克魯斯卡爾 O(mlogm)
luogu3366

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
int father[5010];
int ans,cnt;
struct Node
{
	int a,b,Len;
}Edge[200010];
inline bool cmp(Node a,Node b)
{
	return a.Len<b.Len;
}
int find(int x)
{
	if(father[x]==x) return x;
	return father[x]=find(father[x]);
} 
inline void merge(int x,int y)
{
	father[y]=x;
}
int main()
{
	int x,y,z;
	int fx,fy;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) father[i]=i;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		Edge[i].a=x,Edge[i].b=y,Edge[i].Len=z;
	}
	sort(Edge+1,Edge+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		fx=find(Edge[i].a),fy=find(Edge[i].b);
		if(fx!=fy)
		{
			ans+=Edge[i].Len;
			merge(fx,fy);
			cnt++;
		}
		if(cnt==n-1) break;
	}
	if(cnt==n-1)
	printf("%d",ans);
	else printf("orz");
	return 0;
}

10.prim O(n^2)
luogu3366

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
int Map[5010][5010];
int dis[5010];
int path[5010];
int ans;
void prim(int x)
{
	for(int i=1;i<=n;i++)
	{
		dis[i]=Map[i][x];
		path[i]=x;
	}
	int Min,flag;
	for(int i=1;i<n;i++)
	{
		Min=0x7fffffff;
		for(int j=1;j<=n;j++)
		{
			if(dis[j]&&Min>dis[j])
			{
				Min=dis[j];
				flag=j;
			}
		}
		dis[flag]=0;
		for(int j=1;j<=n;j++)
		{
			if(dis[j]>Map[j][flag])
			{
				dis[j]=Map[j][flag];
				path[j]=flag;
			}
		}
	}
	for(int i=1;i<=n;i++)
	if(path[i]!=i) ans+=Map[i][path[i]];
	printf("%d",ans);
}
int main()
{
	int x,y,z;
	scanf("%d%d",&n,&m);
	memset(Map,0x3f,sizeof(Map));
	for(int i=1;i<=n;i++) Map[i][i]=0;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		Map[x][y]=Map[y][x]=min(Map[x][y],z);
	}
	prim(1);
	return 0;
}

11.Tarjan求有向圖強連通分量
(1).在樹上從根結點出發做一次DFS,記錄每個結點的dfn值和後代的數量。設u有K個後代(不包括u自己)。如果滿足:dfn[u] < dfn[v] <= dfn[u] + K 則表明u是v(v不是u)的祖先
資訊學奧賽一本通1383

#include<cstdio>
#include<vector>
#include<stack>
using namespace std;
vector<int> G[210];
stack<int> s;
int ans,n,m,visitime,dfn[210],low[210],scc,Belong[210];
bool Instack[210],flag[210];
inline int _min(int a,int b)
{
	if(a<b) return a;
	return b;
}
inline void Tarjan(int x)
{
	dfn[x]=low[x]=++visitime;
	s.push(x);
	Instack[x]=1;
	for(int i=0;i<G[x].size();i++)
	{
		int j=G[x][i];
		if(dfn[j]==0)
		{
			Tarjan(j);
			low[x]=_min(low[x],low[j]);
		}
		else if(dfn[j]!=0&&Instack[j])
		{
			low[x]=_min(low[x],dfn[j]);
		}
	}
	if(dfn[x]==low[x])
	{
		int v;
		scc++;
		do
		{
			v=s.top();
			s.pop();
			Instack[v]=0;
			Belong[v]=scc;
		}
		while(x!=v);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int a;
		while(scanf("%d",&a)==1&&a!=0)
		{
			G[i].push_back(a);
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(Belong[i]==0)
		Tarjan(i);
	}
	for(int i=1;i<=n;i++)
	{
		int j;
		for(j=0;j<G[i].size();j++)
		{
			int k=G[i][j];
			if(Belong[i]!=Belong[k])
			{
				flag[Belong[k]]=1;
			}
		}
	}
	for(int i=1;i<=scc;i++)
	{
		if(flag[i]==0) ans++;
	}
	printf("%d",ans);
	return 0;
}

12.最長公共子串
設f[i][j]表示以A串第i個字元和B串第j個字元結尾的最長公共子串(答案:f陣列中最大的一個)

if(A[i]==B[j]) f[i][j]=f[i-1][j-1]+1;
else f[i][j]=0;

13.最長公共子序列
設f[i][j]表示A串前i個字元和B串前j個字元結尾的最長公共子序列(答案:f[A.length][B.length])

if(A[i]==B[j]) f[i][j]=f[i-1][j-1]+1;
else f[i][j]=max(f[i-1][j],f[i][j-1]);

14.01矩陣矩陣:在01矩陣中找出一個最大全1正方形
設f[i][j]表示把點(i,j)作為一個正方形對角線(左上往右下)的右下角端點,能夠得到的最大正方形的邊長

if(a[i][j]==0) f[i][j]=0;
else f[i][j]=min(f[i-1][j-1],f[i-1][j],f[i][j-1])+1;

15.最大子矩陣:在帶權(權值可正可負)矩陣中找出一個權值總和最大的矩陣
資訊學奧賽一本通1282

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
int sum[101][101],f[101][101][101];
int main()
{
    int n,m,i,j,t,k,ans=-0x7fffffff;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            scanf("%d",&t);
            sum[i][j]=t+sum[i-1][j];
        }
    }
    for(i=1;i<=n;i++)
    {
        for(j=i;j<=n;j++)
        {
            for(k=1;k<=n;k++)
            {
                t=sum[j][k]-sum[i-1][k];
                if(f[i][j][k-1]>0) f[i][j][k]=t+f[i][j][k-1];
                else f[i][j][k]=t;
                if(ans<f[i][j][k]) ans=f[i][j][k];
            }
        }
    }
    printf("%d",ans);
    return 0;
}

16.01揹包(倒序迴圈)
(1).使剩餘空間最小
f[i]表示揹包的剩餘體積是否可以為i(f[0]=1,其餘為0)

if(f[i-v[j]]) f[i]=1;

(2).使價值最大(不要求用完容積)
f[i]表示容積為i的揹包能裝下的最大價值(初始化全為0)

f[i]=max(f[i],f[i-v[j]]+value[j])

(3).使價值最大(要求用完容積)
f[i]表示容積為i的揹包能裝下的最大價值(初始化f[0]=0,其餘為負無窮)

f[i]=max(f[i],f[i-v[j]]+value[j])

17.完全揹包(正序迴圈)
(1).使剩餘空間最小
f[i]表示揹包的剩餘體積是否可以為i(f[0]=1,其餘為0)

if(f[i-v[j]]) f[i]=1;

(2).使價值最大(不要求用完容積)
f[i]表示容積為i的揹包能裝下的最大價值

f[i]=max(f[i],f[i-v[j]]+value[j]);

(3).使價值最大(要求用完容積)
參考01揹包

18.二維揹包
f[i][j]表示第一個揹包容積為i,第二個揹包容積為j時可獲得的最大價值

f[i][j]=max(f[i][j],f[i-v[k]][j]+value[k],f[i][j-v[k]]+value[k]);

19.多重揹包
資訊學奧賽一本通1269

#include<cstdio> 
#include<iostream>
using namespace std;
int v[10001],w[10001],s,f[6001];
int main()  
{  
    int n,m,i,a,b,c,j,k;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        for(j=1;j<=c;j<<=1)
        {
            s++;
            v[s]=j*a;
            w[s]=j*b;
            c-=j;
        }
        if(c==0) continue;
        s++;
        v[s]=a*c;
        w[s]=b*c;
    }
    for(i=1;i<=s;i++)
    {
        for(j=m;j>=v[i];j--)
        {
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }
    printf("%d",f[m]);
    return 0;  
}   

20.混合揹包(如果多重揹包要二進位制優化則會佔用太多空間,時間複雜度只能稍優)
資訊學奧賽一本通1270

#include<iostream>  
#include<cstdio>  
using namespace std;
int w[31],c[31],p[31],f[201];
int main()  
{  
    int n,m,i,j,k,temp;
    scanf("%d%d",&m,&n);
    for(i=1;i<=n;i++)
    scanf("%d%d%d",&w[i],&c[i],&p[i]);
    for(i=1;i<=n;i++)
    {
        if(p[i]==0)
        for(j=w[i];j<=m;j++)
        f[j]=max(f[j],f[j-w[i]]+c[i]);
        else
        for(int j=1;j<=p[i];j++)
        for(int k=m;k>=w[i];k--)
        f[k]=max(f[k],f[k-w[i]]+c[i]);
    }
    printf("%d",f[m]);
    return 0;  
}

21.分組揹包
資訊學奧賽一本通1272

#include<iostream>  
#include<cstdio> 
#include<vector>
using namespace std;
int w[31],c[31],p,f[201];
vector<int> G[15];
int main()  
{  
    int n,v,t,i,j,k;
    scanf("%d%d%d",&v,&n,&t);
    for(i=1;i<=n;i++)
    scanf("%d%d%d",&w[i],&c[i],&p),G[p].push_back(i);
    for(i=1;i<=t;i++)//注意迴圈階段
    for(j=v;j>=0;j--)
    for(k=0;k<G[i].size();k++)
    if(j-w[G[i][k]]>=0) f[j]=max(f[j],f[j-w[G[i][k]]]+c[G[i][k]]);
    printf("%d",f[v]);
    return 0;  
}

22.擴充套件歐幾里得
(1).輸入a,b,求ax+by=gcd(a,b)中的一組整數解

#include<bits/stdc++.h>
using namespace std;
int a,b,x,y;
int E_gcd(int a,int b,int &x1,int &y1)
{
	if(b==0)
	{
		x1=1,y1=0;
		return a;
	}
	int x2,y2,d=E_gcd(b,a%b,x2,y2);
	x1=y2;
	y1=x2-a/b*y2;
}
int main()
{
	scanf("%d%d",&a,&b);
	int d=E_gcd(a,b,x,y);
	printf("%d %d",x,y);
	return 0;
} 

(2).ax+by=c是否有整數解
如果 c%gcd(a,b)==0則有解,否則無解
(3).ax+by=c的通解
設用擴歐求出ax1+by1=gcd(a,b)=d的一組解x1,y1,則(k為常數):
x=x1×c/d+k×b/d
y=y1×c/d-k×a/d
最小正整數解:若gcd(a, b) = d,則方程ax ≡ c (mod b)在[0, b/d - 1]上有唯一解
(4).解模線性方程組:ax≡1(mod b)表示ax%b=1%b
則:ax-by=1 用擴歐處理即可 注意判斷是否有解
(5).解模線性方程組:ax≡b (mod n)
即:ax-ny=b 用擴歐處理
(6).解乘法逆元:ax≡1(mod n)的解x稱為a在模n意義下的乘法逆元
則:ax-ny=1 所以gcd(a,n)==1才有逆元
注意:通過擴歐算出的解x必須要mod n,即(x+n)%n才是乘法逆元

int check(int a,int n)
{
	int x,y;
	if(kuoou(a,n,x,y)==1) return (x+n)%n;
	return -1;
}

(7).求(a/b) mod p
解:設b1是b在模p意義下的乘法逆元
則(a/b) mod p == (a×b1) mod p == ((a mod p)×(b1 mod p)) mod p

23.質數
(1).費馬小定理:如果p是質數且gcd(a,p)==1,則ap-1%p == 1
所以ap-2就是a關於p的逆元
(2).對於一個正整數x,小於x且與x互質的正整數的個數叫做尤拉函式,記做φ(x)(φ(1)==1)

求解:φ(x)
公式1:φ(x)==x*(1-1/p1) * (1-1/p2)… * (1-1/pk)
其中:p1,p2…pk為x的所有質因數
NKOJ3550

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll ans;
void phi(ll d)
{
    ans=d;
    ll i,a=d;
    for(i=2;i*i<=a;i++)
    {
        if(a%i==0)
        {
            ans-=ans/i;
            while(a%i==0)
            {
                a/=i;
            }
        }
    }
    if(a>1) ans-=ans/a;
}
int main()
{
    ll n;
    scanf("%lld",&n);
    phi(n);
    printf("%lld",ans);
    return 0;
}

公式2:如果x == pk,則φ(x) == (p-1) * (pk-1)(p為質數)

性質1:若gcd(x,y) == 1,則φ(x*y) == φ(x) * φ(y)
性質2:若x為質數,則φ(x) == x-1

線性篩求尤拉函式

void getphi(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(mark[i]==0) prime[++tot]=i,phi[i]=i-1;
        else//找一個模板題測一下這個else是否要
        {
            for(int j=1;j<=tot&&i*prime[j]<=n;j++)
            {
                mark[i*prime[j]]=1;
                if(i%prime[j]==0)
                {
                    phi[i*prime[j]]=prime[j]*phi[i];
                    break;
                }
                else
                {
                    phi[i*prime[j]]=phi[i]*(prime[j]-1);
                }
            }
        }
    }
}

(3).尤拉定理:若gcd(a,n) == 1,則aφ(n)%n == 1
應用1:若gcd(a,n) == 1,則 ab%n == ab%φ(n)%n (當b很大且n是質數的時候很好用)
應用2:若b>=φ(n),則ab %n== ab%φ(n)+φ(n)%n
(4).中國剩餘定理:
(5).排列:從n個不同的元素中,取m個不重複的元素,按次序排列,稱為從n箇中取m個的排列。
Amn=n!/(n-m)!
(6).組合:從n個不同的元素中,取m個不重複的元素,不考慮次序,稱為從n箇中取m個的組合
Cmn=n!/(n-m)!/m!

性質1:Cmn=Cn-mn
性質2:C0n+C1n+C2n…+Cnn=2n
性質3:Cmn=Cmn-1+Cm-1n-1

long long C[1000][1000];
for(int i=1;i<=n;i++)C[i][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
C[i][j] = (C[i-1][j]+C[i-1][j-1]) % k;

盧卡斯定理:計算Cmn%p(p是質數)

ll C(ll a,ll b)
{
    if(a<b) return 0;
    if(a==b) return 1;
    if(b>a-b) b=a-b;
    ll A=1,B=1;
    for(ll i=0;i<b;i++)
    {
        A = (A * (a - i)) % p;
        B = (B * (b - i)) % p;
    }
    return A * quickpow(B,p-2,p) %p;
}
ll Lucas(ll n,ll m)
{
    if(m==0) return 1;
    return C(n%p,m%p) * Lucas(n/p,m/p) %p;
}

(7).二項式定理:(x+y)n=C0n * xn * y0+C1n * xn-1 * y1+…Cnn * x0 * yn

(8).第二類斯特林數:第二類斯特林數S2[n][m]表示把n個元素劃分成m個非空集合的方案數。
S2[n][m] = S2[n-1][m-1] + m * S2[n-1][m]

void getStirling()
{
for(i=1;i<=n;i++)s2[i][1]=1;
for(int i=1;i<=n;i++)
for(int j=2;j<=i&&j<=m;j++)
s2[i][j]=(s2[i-1][j-1]+j*s2[i-1][j])%mod;
}

(9).第一類斯特林數:第一類斯特林數S1[n][m]表示把n個元素劃分成m個非空迴圈排列集合的方案數。
S1[n][m] = S1[n-1][m-1] + (n-1) * S1[n-1][m]

void Stirling1(int n,int m){
S1[1][1]=1;
for(int i=2;i<=n;i++)
    for(int j=1;j<=i && j<=m;j++)
        S1[i][j]=S[i-1][j-1]+(i-1)*S[i-1][j];}

(10).bell數:BELL數B[n]表示把n個元素劃分成若干個非空集合的方案數。
B[n] = S2[n][1]+S2[n][2]+S2[n][3]+ … +S2[n][n]
(11).判斷質數之miller_rabin
輸入多組資料,每組資料有n個正整數,判斷有多少個質數

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,a,ans;
ll quickpow(ll A,ll b,ll c)
{
	ll Ans=1;
	while(b)
	{
		if(b&1) Ans=(Ans*A)%c;
		A=(A*A)%c;
		b>>=1;
	}
	return Ans;
}
bool Miller_Rabin(ll k)
{
	ll d,r=0,x,y,z;
	if(k==2) return 1;
	if((k&1)==0||k<2) return 0;
	d=k-1;
	while((d&1)==0)
	{
		r++;
		d/=2;
	}
	for(int i=1;i<=10;i++)
	{
		z=rand()%(k-2)+2;
		x=quickpow(z,d,k);
		for(ll j=1;j<=r;j++)
		{
			y=(x%k*(x%k))%k;
			if(y==1&&x!=1&&x!=k-1) return 0;
			x=y;
		}
		if(x!=1) return 0;
	}
	return 1;
}
int main()
{
	while(scanf("%lld",&n)==1)
	{
		ans=0;
		for(ll i=1;i<=n;i++)
		{
			scanf("%lld",&a);
			if(Miller_Rabin(a)) ans++;
		}
		printf("%lld\n",ans);
	}
    return 0;   
}  

24.歸併排序

#include<iostream> 
#include<cstdio> 
#include<cmath> 
#include<algorithm> 
#include<cstring>
using namespace std;
int a[500010],b[500010];
long long ans;
int n;
void my_sort2(int l,int mid,int r)
{
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r)
    {
        if(a[i]<=a[j])
        {
            b[k]=a[i];
            k++;
            i++;
        }
        else
        {
            b[k]=a[j];
            k++;
            ans+=(long long)mid-i+1;
            j++;
        }
    }
    while(i<=mid) 
    {
        b[k]=a[i];
        k++;
        i++;
    }
    while(j<=r) 
    {
        b[k]=a[j];
        k++;
        j++;
    }
    for(i=l;i<=r;i++) a[i]=b[i];
}
void my_sort1(int l,int r)
{
    if(l==r) return;
    int mid=(l+r)>>1;
    my_sort1(l,mid);
    my_sort1(mid+1,r);
    my_sort2(l,mid,r);
}
int main() 
{ 
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    my_sort1(1,n);
    printf("%lld",ans);
    return 0; 
}

25.快速冪

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll A,B,C;
ll quickpow(ll a,ll b,ll c)
{
    ll ans=1;
    for(;b;b>>=1)
    {
        if(b&1) ans=ans*a%c;
        a=a*a%c;
    }
    return ans;
}
int main()
{
	scanf("%lld%lld%lld",&A,&B,&C);
	ll ans=quickpow(A,B,C);
	printf("%lld",ans);
    return 0;
}

26.列舉子集(s1是s的子集)

for(int s1=s;s1;s1=(s1-1)&s)
{
	//do something
}

27.高精度
(1).加

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
string s,y;
string jiafa(string x,string y)
{
	int i,l1=x.length(),l2=y.length(),a[201],b[201],c[202],flag=0,x1=0;
	string s1="";
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(c,0,sizeof(c));
	for(i=0;i<l1;i++) a[l1-i]=int(x[i]-48);
	for(i=0;i<l2;i++) b[l2-i]=int(y[i]-48);
	i=1;
	while(i<=l1||i<=l2)
	{
		c[i]=a[i]+b[i]+x1;
		x1=c[i]/10;
		c[i]%=10;
		i++;
	}
	c[i]=x1;
	if(c[i]==0) i--;
	for(;i>=1;i--)
	{
		s1+=char(c[i]+48);
	}
	return s1;
}
int main()
{
	cin>>s>>y;
	string s1=jiafa(s,y);
	cout<<s1;
	return 0;
}

(2).減

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
void jianfa(char x[201],char y[201])
{
	int i,lx=strlen(x),ly=strlen(y),a[201],b[201],c[201],c1=0;
	char z[201];
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(c,0,sizeof(c));
	if(lx==ly&&strcmp(x,y)==-1||lx<ly)
	{
		strcpy(z,x);
		strcpy(x,y);
		strcpy(y,z);
		printf("-");
	}
	lx=strlen(x);
	ly=strlen(y);
	for(i=0;i<lx;i++) a[lx-i]=int(x[i]-48);
	for(i=0;i<ly;i++) b[ly-i]=int(y[i]-48);
	i=1;
	while(i<=lx||i<=ly)
	{
		if(a[i]-b[i]<0)
		{
			a[i]+=10;
			a[i+1]--;
		}
		c[i]=a[i]-b[i];
		i++;
	}
	while(c[i]==0&&i>1) i--;
	for(;i>=1;i--)
	printf("%d",c[i]);
}
int main()
{
	char a[201],b[201];
	scanf("%s%s",&a,&b);
	jianfa(a,b);
	return 0;
}

(3).乘

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
void jiafa(char x[201],char y[201])
{
	int i,a[201],b[201],c[202],x1=0,lx=strlen(x),ly=strlen(y),j;
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(c,0,sizeof(c));
	for(i=0;i<lx;i++) a[lx-i]=int(x[i]-48);
	for(i=0;i<ly;i++) b[ly-i]=int(y[i]-48);
	for(i=1;i<=lx;i++)
	{
		x1=0;
		for(j=1;j<=ly;j++)
		{
			c[i+j-1]+=a[i]*b[j]+x1;
			x1=c[i+j-1]/10;
			c[i+j-1]%=10;
		}
		c[i+ly]=x1;
	}
	i=lx+ly;
	while(c[i]==0&&i>1) i--;
	for(;i>=1;i--)
	printf("%d",c[i]);
}
int main()
{
	char x[201],y[201];
	scanf("%s%s",&x,&y);
	jiafa(x,y);
	return 0;
}