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)
NKOJ1120

#include<bits/stdc++.h>
using namespace std;
int n,m;
int dis[101][101];
void floyd()
{
	int i,j,k;
	for(k=1;k<=n;k++)
	{
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=n;j++)
			{
				if(dis[i][j]>dis[i][k]+dis[k][j])
				{
					dis[i][j]=dis[i][k]+dis[k][j];
				}
			}
		}
	}
}
int main()
{
	int i,x,y;
	scanf("%d%d",&n,&m);
	memset(dis,0x3f,sizeof(dis));
	for(i=1;i<=m;i++)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		dis[a][b]=c;
	}
	scanf("%d%d",&x,&y);
	floyd();
	printf("%d",dis[x][y]);
	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)
luogu1546

#include<bits/stdc++.h>
using namespace std;
int n,dis[110],Map[110][110],path[110],ans;
void prim(int s)
{
    for(int i=1;i<=n;i++)
    {
        dis[i]=Map[i][s];
        path[i]=s;
    }
    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]];
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            int dis;
            scanf("%d",&dis);
            Map[i][j]=dis;
        }
    }
    prim(1);
    printf("%d",ans);
    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
        {
            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 getStirling()
{
for(i=0;i<=n;i++)s[i][i]=1;
for(i=1;i<=n;i++)
for(j=1;j<=m&&j<=i;j++)
s[i][j]=(s[i-1][j-1]+(i-1)*s[i-1][j])%mod;
}

(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;   
}  

猜你喜欢

转载自blog.csdn.net/dingxingdi/article/details/82938706