NOIP 信息学 奥赛 考纲 考点 模板 裸题 水题

NOIP知识点汇总
加*号是选学,加粗为重点,重要值排序不分先后

基础算法

贪心、枚举、分治、二分、倍增、*构造、高精、模拟
图论

图 
最短路(dijkstra、spfa、floyd),差分约束
最小生成树(kruskal、prim)
并查集(扩展域)
拓扑排序
二分图染色,*二分图匹配
tarjan找scc、桥、割点,缩点
*分数规划
树 
树上倍增(LCA)
树的直径、树的重心
dfs序
*树链剖分
数论

gcd、lcm
埃氏筛法
exgcd,求解同余方程、逆元
快速幂
*组合数学
矩阵
数据结构

链表、队列(单调队列)、栈(单调栈)
堆、st表、hash表
线段树、树状数组
字典树
*分块
动态规划

背包DP、树形DP、记忆化搜索、递推
区间DP、序列DP
*DP优化(不涉及斜率优化、四边形不等式等等)
搜索

暴搜(dfs、bfs)
搜索的剪枝
启发式搜索(A*)
迭代加深搜索、* IDA*
*随机化搜索
其他算法

STL的基本使用方法
脑洞的正确使用方法
*KMP
*状态压缩

省选知识点汇总
冲省选的,先把整理的NOIP知识点学扎实,注意一定要学扎实 
加粗是重点,星号是选学 
学无止境,欢迎大家继续补充~

图论

网络流(dinic,SAP,ISAP选一个,费用流写EK就行。*zkw费用流),二分图
点分治,边分治,*动态点分治
树链剖分,动态树,树分块
虚树,*prufer编码
*仙人掌算法
数据结构

带权并查集
Splay(作为平衡树和维护区间),Treap,替罪羊树
线段树(权值线段树),树状数组,*线段树合并
分块,块状链表,*双向链表
凸包
树套树
主席树,可持久化trie,*其它可持久化数据结构
莫队算法,*树上莫队,CDQ分治,整体二分
二维线段树,*KDtree
*舞蹈链,*二进制分组,*左偏树,*超哥线段树,*后缀平衡树,*fhqTreap
字符串相关算法及数据结构

hash(自然溢出,双hash)
kmp,AC自动机,trie
后缀数组
manacher,最小表示法
*后缀自动机,*回文自动机,*后缀树
数学

线性筛,积性函数,容斥原理,莫比乌斯反演
exgcd,费马小定理,Lucas定理,高中排列组合
高斯消元,概率与期望相关
中国剩余定理,BSGS,欧拉定理
矩阵乘法
单纯形法解线性规划
FFT
线性代数(行列式)
*Simpson积分,高中求导与积分
*群论
*生成函数, 多项式类算法
博弈论相关,*密码学,阶,原根
计算几何

向量的点积/叉积,计算几何基础
*二维计算几何相关,*三维计算几何相关
*半平面交,*旋转卡壳,*三角剖分
搜索

A*,记忆化搜索,迭代深搜,双向广搜
模拟退火,爬山算法,*随机增量法
动态规划

基础DP,树形DP,数位DP,状压DP,期望DP,基环树DP,*插头DP
斜率优化,矩乘优化,单调队列优化,倍增优化,*四边形不等式优化
trie图DP,*仙人掌DP
其他算法

构造,乱搞,随机化,三分法,打表,启发式合并
Huffman树,2-sat,*朱刘算法
说真的,计算几何要么全场不会,要么全场AK。所以尽量花时间在别的地方吧。

以下是针对考点提供的裸题,例题,希望能加快读者理解掌握考点。2018-3-16 16:59

NOIP知识点汇总
加*号是选学,加粗为重点,重要值排序不分先后

基础算法

贪心、枚举、分治、二分、倍增、*构造、高精、模拟
图论

图 
最短路(dijkstra、spfa、floyd),
差分约束
//P1645 序列
//差分约束
//样例通过,提交,测试点3,4,6WA
//经过对比,发现,还是左边界的问题。
//提交AC 2018-5-9
#include <stdio.h>
#include <string.h>
struct node{
    int to,next,w;
}e[1000+10002+100];
int cnt=0,head[1100],d[1100],vis[1100],q[1100
10],R=-1999999999;
int max(int a,int b){
    return a>b?a:b;
}
void add_edge(int u,int v,int w){
    cnt++,e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt;//竟然漏了e[cnt].w=w;这句
}
void spfa(int x){
    int h,t,i,b,u,v,w;
    memset(vis,0,sizeof(vis));
    for(i=0;i<=R;i++)d[i]=-1999999999;
    h=t=1;
    q[t]=x,t++,vis[x]=1,d[x]=0;
    while(h<t){
        u=q[h],vis[u]=0,b=head[u];
        while(b){
            v=e[b].to,w=e[b].w;
            if(d[v]<d[u]+w){
                d[v]=d[u]+w;
                if(vis[v]==0){
                    vis[v]=1;
                    q[t]=v;
                    t++;
                }
            }
            b=e[b].next;
        }
        h++;
    }
}
int main(){
    int u,v,w,i,n;
    memset(head,0,sizeof(head));
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d%d%d",&u,&v,&w);
        R=max(R,v),add_edge(u-1,v,w);//此处写成 add_edge(u+1,v+1,w)
    }
    for(i=0;i<=R;i++)
        add_edge(i-1,i,0);
    for(i=0;i<=R;i++)
        add_edge(i,i-1,-1);
    spfa(0);
    printf("%d",d[R]);
    return 0;
}
试了:随机数据生成与对拍 但发现 该题数据 还想遵循一定规则,故随机数据生成 暂告 搁置 2018-5-10
 
最小生成树(kruskal、prim)
并查集(扩展域)
拓扑排序
二分图染色,*二分图匹配
tarjan找scc、桥、割点,缩点
*分数规划
树 
树上倍增(LCA):
http://blog.csdn.net/saramanda/article/details/54963914此文介绍得不错,可以作为初步了解,但又一个疑问,适用提交,满二叉树,完全二叉树,还是普通的二叉树。
https://www.cnblogs.com/yyf0309/p/5972701.html本人偏好此文,有图有程序。
树的直径:
poj2631 求树的直径裸题
https://www.cnblogs.com/a-clown/p/6131346.html此文介绍得不错
http://poj.org/problem?id=2631可见该题
https://vjudge.net/problem/POJ-2631  可见该题
带着问题学习,明显效率高了很多,https://www.cnblogs.com/jsawz/p/6809817.html该文有树的直径概念及代码 ,经样例模拟,发现此文树的直径代码有误,起始点疏漏了访问标记,造成多次访问。2018-3-21
树上最长的简单路径即为树的直径。

求树的直径的方法就是在树上任选一点u,求距离点u最远的点y,再求距离点y最远的点s,点y到点s的距离即为树的直径。

//poj 2631 Roads in the North
//树的直径 裸题
//该题没说道路的数量范围,采用C(n,2)
//采用邻接表存储 数据
//样例通过,提交AC 2018-3-18 20:27
//1.树的直径
//树上最长的简单路径即为树的直径。
//求树的直径的方法就是在树上任选一点u,求距离点u最远的点y,再求距离点y最远的点s,点y到点s的距离即为树的直径。2018-3-21 AC
#include <stdio.h>
#include <string.h>
int d[10100],head[10100],cnt=0,vis[10100];//没设置访问标志vis[10100],造成了理解困难
struct node{
    int to,next,w;
}e[10100*10100/2];
int max(int a,int b){
    return a>b?a:b;
}
void add_edge(int u,int v,int w){
    cnt++,e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt;
}
void dfs(int x){
    int v,b;
    b=head[x];
    while(b){
        v=e[b].to;
        if(!vis[v])d[v]=d[x]+e[b].w,vis[v]=1,dfs(v);//此处写成 if(!d[v])d[v]=d[x]+e[b].w,dfs(v);
        b=e[b].next;
    }
}
int main(){
    int u,v,w,n=0,y;
    memset(head,0,sizeof(head));
    while(scanf("%d%d%d",&u,&v,&w)!=EOF)n=max(max(u,v),n),add_edge(u,v,w),add_edge(v,u,w);//无向图
    memset(vis,0,sizeof(vis)),memset(d,0,sizeof(d));
    vis[1]=1,dfs(1);//注意vis[1]=1别忘了
    for(u=y=1;u<=n;u++)if(d[u]>d[y])y=u;
    memset(vis,0,sizeof(vis)),memset(d,0,sizeof(d));
    vis[y]=1,dfs(y);//注意vis[y]=1别忘了
    for(u=y=1;u<=n;u++)if(d[u]>d[y])y=u;
    printf("%d",d[y]);
    return 0;
}
 
树的重心
http://blog.csdn.net/acdreamers/article/details/16905653此文介绍得不错
https://www.cnblogs.com/jsawz/p/6809817.html此文介绍了树的重心概念
若有一点,其所有子树中最大子树的节点数最少,则该点就是这棵树的重心。

一般的树只有一个重心,有些有偶数个节点的树,有两个节点。

求树的重心方法就是随意确定一个根节点,先把无根树转化为有根树,dfs求出所有点的子树的节点个数。如果有一点满足该点的子树的节点数的二倍大于等于总结点数(size[u]*2>=n),并且该点的儿子都满足子树的节点数的二倍小于等于总结点数(size[son_u]*2<=n),这个点就是树的重心。

poj 1655 Balancing Act
https://vjudge.net/problem/POJ-1655可见该题
https://www.cnblogs.com/kuangbin/p/3427267.html此文代码写得不错
http://blog.csdn.net/bigbigship/article/details/47261031此文代码也写得不错
//poj-1655
//样例通过,提交AC 2018-3-21 17:34
#include <stdio.h>
#include <string.h>
#define maxn 20100
int son[maxn],cnt,head[maxn],vis[maxn],num,n,ans;//此处写成 head[maxn]
struct node{
    int to,next;
}e[maxn2];//此处写成 e[maxnmaxn/2]
void add_edge(int u,int v){//邻接表
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
int max(int a,int b){
    return a>b?a:b;
}
void dfs(int x){
    int b,v,maxson=0;//maxson是临时变量,而非全局变量
    son[x]=1,b=head[x];
    while(b){
        v=e[b].to;
        if(!vis[v])vis[v]=1,dfs(v),son[x]+=son[v];
        maxson=max(maxson,son[v]);
        b=e[b].next;
    }
    maxson=max(maxson,n-son[x]);//此处写成maxson=max(maxson,n-maxson);//最大子树节点数
    if(maxson<num||maxson==num&&ans>x)num=maxson,ans=x;
}
int main(){
    int T,i,u,v;
    scanf("%d",&T);
    while(T–){
        memset(head,0,sizeof(head)),memset(vis,0,sizeof(vis)),num=999999999,n,ans=999999999,cnt=0;//漏了cnt=0一直Runtime Error//注意,每次num=999999999,n,ans=999999999都要初始化
        scanf("%d",&n);
        for(i=1;i<=n-1;i++)scanf("%d%d",&u,&v),add_edge(u,v),add_edge(v,u);//无向图
        vis[1]=1,dfs(1);
        printf("%d %d\n",ans,num);//此处写在while循环之外,真是昏招
    }
    return 0;
}

dfs序
*树链剖分
数论

gcd、lcm
埃氏筛法
exgcd,
//P1516 青蛙的约会
//https://www.luogu.org/problemnew/show/P1516可提交测评
//0 < L < =2100000000已经接近int的极限,该题考虑用long long
//一段时间的数论学习下来,还是小有成就的,理解力,推导能力大幅度的上了一个台阶,读者若想好好学习,可参看笔者文章
//https://blog.csdn.net/mrcrack/article/details/80352151
//以下为AC代码,一次编写成功。2018-6-18 18:59
#include <stdio.h>
#define LL long long
LL gcd(LL a,LL b){
    return b?gcd(b,a%b):a;
}
LL exgcd(LL a,LL b,LL *x,LL *y){
    LL t,d;
    if(b==0){
        *x=1,*y=0;
        return a;
    }
    d=exgcd(b,a%b,x,y);
    t=*x,*x=y,y=t-a/b**y;
    return d;
}
int main(){
    LL X,Y,M,N,L;
    LL a,b,c,d;
    LL a0,b0,c0;
    LL x0,y0,x1;
    scanf("%lld%lld%lld%lld%lld",&X,&Y,&M,&N,&L);
    a=((N-M)%L+L)%L,b=L,c=((X-Y)%L+L)%L;
    d=gcd(a,b);
    if(c%d){
        printf(“Impossible”);
        return 0;
    }
    a0=a/d,b0=b/d,c0=c/d;
    exgcd(a0,b0,&x0,&y0);
    x0
=c0,y0
=c0;
    x1=(x0%b0+b0)%b0;
    printf("%lld",x1);
    return 0;
}

//p1516 青蛙的约会
//https://www.luogu.org/problemnew/show/P1516可提交测评
//扩展欧几里得算法
//是否需要考虑,正负数?
//该题,比较简单的就是,输出Impossible,猜测能拿到20分
//考虑了,还是采用long long比int胜算大
//以下为运用了 求解线性同余方程 性质,何时有解,何时无解,提交20分,计算过程中没有顾及负数。满意2018-5-16
//样例通过,提交AC 2018-5-16 17:30 以下为AC 代码
#include <stdio.h>
#define LL long long
LL X,Y,M,N,L,a,b,c;
LL gcd(LL a,LL b){
    return b?gcd(b,a%b):a;
}
LL exgcd(LL a,LL b,LL *x,LL *y){
    LL t,d;
    if(b==0){
        *x=1,*y=0;//此处写成 *x=c/a
        return a;
    }
    d=exgcd(b,a%b,x,y);
    t=*x,*x=y,y=t-a/b**y;
    return d;
}
int main(){
    LL x,y,t,d;
    scanf("%lld%lld%lld%lld%lld",&X,&Y,&M,&N,&L);
    a=((N-M)%L+L)%L,b=L,c=((X-Y)%L+L)%L;//此处写成 a=N-M,b=L,c=X-Y;
    if(c%gcd(a,b)){
        printf(“Impossible”);
        return 0;
    }
    d=exgcd(a,b,&x,&y);
    t=b/d;
    x=(c/d
x%t+t)%t;//此处写成 x=(c
x%t+t)%t; 只有50分 ,修改,只有70分
    printf("%lld",x);
    return 0;
}

//p1516 青蛙的约会
//https://www.luogu.org/problemnew/show/P1516可提交测评
//扩展欧几里得算法
//是否需要考虑,正负数?
//该题,比较简单的就是,输出Impossible,猜测能拿到20分
//考虑了,还是采用long long比int胜算大
//以下为运用了 求解线性同余方程 性质,何时有解,何时无解,提交20分,计算过程中没有顾及负数。满意2018-5-16 
#include <stdio.h>
#define LL long long
LL X,Y,M,N,L,a,b,c;
LL gcd(LL a,LL b){
    return b?gcd(b,a%b):a;
}
int main(){
    scanf("%lld%lld%lld%lld%lld",&X,&Y,&M,&N,&L);
    a=M-N,b=L,c=Y-X;
    if(c%gcd(a,b)){
        printf(“Impossible”);
        return 0;
    }
    return 0;
}
//p1516 青蛙的约会
//https://www.luogu.org/problemnew/show/P1516可提交测评 
//扩展欧几里得算法
//是否需要考虑,正负数?
//该题,比较简单的就是,输出Impossible,猜测能拿到20分
//考虑了,还是采用long long比int胜算大
//以下为20分代码,满意 2018-5-16
#include <stdio.h>
int main(){
    printf(“Impossible”);
    return 0;

求解同余方程、逆元
快速幂
*组合数学
矩阵
数据结构

链表、
队列(单调队列)、
//P1886 滑动窗口
//在线测评地址https://www.luogu.org/problemnew/show/P1886
//先纯模拟,O(n^2),估计最多得10分,样例很快通过,提交
//测试点8-10 TLE,还是那句话,数据太水了。2018-12-25
#include <stdio.h>
#define maxn 1000100
int a[maxn],b[maxn],c[maxn],b_min,c_max;//b最小值c[最大值
int min(int a,int b){
    return a<b?a:b;
}
int max(int a,int b){
    return a>b?a:b;
}
int main(){
    int n,k,i,j,cnt=0;
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(i=1;i<=n-k+1;i++){
        b_min=c_max=a[i];
        for(j=i;j<=i+k-1;j++){
            b_min=min(b_min,a[j]);
            c_max=max(c_max,a[j]);
        }
        cnt++,b[cnt]=b_min,c[cnt]=c_max;    
    }
    for(i=1;i<=cnt;i++)
        printf("%d “,b[i]);
    printf(”\n");
    for(i=1;i<=cnt;i++)        
        printf("%d ",c[i]);
    return 0;
}

//P1886 滑动窗口
//在线测评地址https://www.luogu.org/problemnew/show/P1886
//AC。2018-12-25
#include <stdio.h>
#include <string.h>
#define maxn 1000100
int q[maxn],a[maxn];//存储序列
int main(){
    int n,k,i,h,t;
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    h=t=1,q[t]=1,t++;//此处写成 q[t]=a[1]
    for(i=1;i<=n;i++){//此处写成 for(i=1;i<=n-k+1;i++)
        while(h<t&&a[q[t-1]]>=a[i])t–;//插入队列
        q[t]=i,t++;
        while(h<t&&q[h]+k-1<i)h++;//清理队列
        if(i>=k)//漏了此句
            printf("%d “,a[q[h]]);
    }
    printf(”\n");
    h=t=1,q[t]=1,t++;//此处写成 q[t]=a[1]
    for(i=1;i<=n;i++){//存最大值
        while(h<t&&a[q[t-1]]<=a[i])t–;
        q[t]=i,t++;
        while(h<t&&q[h]+k-1<i)h++;
        if(i>=k)printf("%d ",a[q[h]]);
    }
    return 0;
}

P1886 滑动窗口
https://www.luogu.org/problemnew/show/P1886
//P1886 滑动窗口
//https://www.luogu.org/problemnew/show/P1886可见该题
//https://vjudge.net/problem/POJ-2823可见该题
//读题发现看不懂,仔细看了题中的模拟过程,还是看懂了
//读题,发现该题可以模拟出来,但是会超时
//50%的数据,n<=10^5 100%的数据,n<=10^6
//可以预测 模拟 大约30分
//用模拟代码提交,只有测试点9 TLE 不敢想象啊 2018-3-23
//P1886 滑动窗口
//提交,测试点1-8,10WA
//仔细一看,样例都没通过,确实看走眼了
//样例通过,提交AC 2018-3-23 21:20
#include <stdio.h>
#define maxn 1000100
int a[maxn],q[maxn],h,t;
int main(){
    int i,j,n,k;
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    h=t=1;
    for(i=1;i<=n;i++){//最小值
        while(h<t&&a[q[t-1]]>a[i])t–;
        q[t]=i,t++;
        while(h<t&&q[h]+k-1<i)h++;//此处写成 while(h<t&&q[h]+k<i)h++;
        if(i>=k)printf("%d “,a[q[h]]);
    }
    printf(”\n"),h=t=1;
    for(i=1;i<=n;i++){//最大值
        while(h<t&&a[q[t-1]]<a[i])t–;
        q[t]=i,t++;
        while(h<t&&q[h]+k-1<i)h++;
        if(i>=k)printf("%d “,a[q[h]]);
    }
    return 0;
}
以下代码为90分 纯枚举算法
 
//此文代码写得不错https://www.cnblogs.com/wawcac-blog/p/6986710.html
#include <stdio.h>
#define maxn 1000100
#define INF 999999999
int a[maxn],min[maxn],max[maxn];
int main(){
    int n,k,i,j,b,c;//b最小 c最大
    scanf(”%d%d",&n,&k);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    for(i=1;i<=n-k+1;i++){
        b=INF,c=-INF;
        for(j=i;j<=i+k-1;j++){
            if(a[j]<b)b=a[j];
            if(a[j]>c)c=a[j];
        }
        min[i]=b,max[i]=c;
    }
    for(i=1;i<=n-k+1;i++)printf("%d “,min[i]);
    printf(”\n");
    for(i=1;i<=n-k+1;i++)printf("%d “,max[i]);    
    return 0;
}
//hdu 3530 Subsequence
//https://vjudge.net/problem/HDU-3530可见该题
//单调队列
//看了半天题目,竟然没看懂,看了中文题目,才发现the difference between指的是 差值 而不是 不同
//弄明白题意,开始考虑该题思路
//https://blog.csdn.net/zxf654073270/article/details/42712351此文代码写得不错
//https://blog.csdn.net/dan__ge/article/details/51746590此文代码也写得不错
//提交,Compilation Error,发现要将中文注释删除,将中文注释全部删除后,提交AC 2018-3-24
//故下面代码要想AC,需将中文注释全部删除.
#include <stdio.h>
#define maxn 100100
int a[maxn],q1[maxn],q2[maxn],h1,t1,h2,t2,pos;
int max(int a,int b){
    return a>b?a:b;
}
int main(){
    int n,m,k,i,ans;
    while(scanf(”%d%d%d",&n,&m,&k)!=EOF){
        h1=t1=1,h2=t2=1,pos=0,ans=0;
        for(i=1;i<=n;i++)scanf("%d",&a[i]);
        for(i=1;i<=n;i++){
            while(h1<t1&&a[q1[t1-1]]>a[i])t1–;
            while(h2<t2&&a[q2[t2-1]]<a[i])t2–;
            q1[t1++]=i,q2[t2++]=i;
            while(h1<t1&&h2<t2&&a[q2[h2]]-a[q1[h1]]>k)
                if(q1[h1]<q2[h2])pos=q1[h1++];
                else pos=q2[h2++];
            if(h1<t1&&h2<t2&&a[q2[h2]]-a[q1[h1]]>=m)ans=max(ans,i-pos);
        }
        printf("%d\n",ans);
    }
    return 0;
}

以下代码包含中文注释,无法AC,仅供参考之用
#include <stdio.h>
#define maxn 100100
int a[maxn],q1[maxn],q2[maxn],h1,t1,h2,t2,pos;
int max(int a,int b){
    return a>b?a:b;
}
int main(){
    int n,m,k,i,ans;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF){
        h1=t1=1,h2=t2=1,pos=0,ans=0;//请注意pos初始化很重要,pos=0而非1
        for(i=1;i<=n;i++)scanf("%d",&a[i]);
        for(i=1;i<=n;i++){
            while(h1<t1&&a[q1[t1-1]]>a[i])t1–;//最小值 由小到大 队列
            while(h2<t2&&a[q2[t2-1]]<a[i])t2–;//最大值 由大到小 队列
            q1[t1++]=i,q2[t2++]=i;
            while(h1<t1&&h2<t2&&a[q2[h2]]-a[q1[h1]]>k)
                if(q1[h1]<q2[h2])pos=q1[h1++];
                else pos=q2[h2++];
            if(h1<t1&&h2<t2&&a[q2[h2]]-a[q1[h1]]>=m)ans=max(ans,i-pos);//此处写成 if(h1<t1&&h2<t2&&q2[h2]-q1[h1]<=m)ans=max(ans,i-pos);真是昏招啊
        }
        printf("%d\n",ans);
    }
    return 0;
}
 
栈(单调栈)
//P2866 [USACO06NOV]糟糕的一天Bad Hair Day
//在线测评地址https://www.luogu.org/problemnew/show/P2866 
//先试试纯模拟O(n^2) 
//(8000+0)8000/2=6.410^9
//个数要采用long long
//样例通过,提交,测试点2,8,10TLE 70分,还算满意,同时说明数据挺水。
//2018-12-26 20:43 
#include <stdio.h>
#define LL long long
LL cnt=0;
int h[80100];
int main(){
    int n,i,j;
    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%d",&h[i]);
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
            if(h[i]>h[j])cnt++;
            else break;
    printf("%lld\n",cnt);
    return 0;
}

//P2866 [USACO06NOV]糟糕的一天Bad Hair Day
//在线测评地址https://www.luogu.org/problemnew/show/P2866 
//先试试纯模拟O(n^2) 
//(8000+0)8000/2=6.410^9
//个数要采用long long
//样例通过,提交,测试点2,8,10TLE 70分,还算满意,同时说明数据挺水。
//2018-12-26 20:43
//开一个栈,a[i]元素必需置于栈顶,放入之前,top的数值,表示a[i]能被之前的几头牛看见。 
//样例通过,提交AC。2018-21-26 21:00 
#include <stdio.h>
#define LL long long
LL cnt=0;
int h[80100],stack[80100],top=0;
int main(){
    int n,i;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d",&h[i]);
        while(top>0&&stack[top]<=h[i])top–;//弹出不能看见a[i]的牛
        cnt+=top;
        stack[++top]=h[i]; 
    }
    printf("%lld\n",cnt);
    return 0;
}

//poj 3250 Bad Hair Day
//https://vjudge.net/problem/POJ-3250可见该题
//首先采用纯模拟
//程序编写比较容易,提交也出现了意料中的Time Limit Exceeded
//这种情况比较常见,需要学习特定算法,单调栈
//https://blog.csdn.net/alongela/article/details/8229702该文思路介绍得不错,思路摘抄如下:
//从左到右依次读取当前牛的高度,从栈顶开始把高度小于或等于当前牛的高度的那些元素删除,此时栈中剩下的元素的数量就是可以看见当前牛的其他牛的数量。把这个数量加在一起,就可以得到最后的答案了。
//仔细想想,这个思路,不通过专门训练,很难想到
//样例通过,提交Wrong Answer
//仔细思考,(80000+0)/280000=32108=3.2*109 int要溢出,果断改成long long
//提交Wrong Answer
//无奈,https://www.luogu.org/problemnew/show/P2866提交,竟然AC ,没想法啊
//http://codevs.cn/problem/3098/提交,还是AC
//https://vjudge.net/problem/POJ-3250提交,AC,真是奇怪了,代码没改过啊!2018-3-26 20:56
//调出提交的代码,才发现,测试语句未删除,确实有误。2018-3-26 21:00
#include <stdio.h>
#define maxn 80100
int a[maxn],n,stack[maxn],top=0;
long long cnt=0;
int main(){
    int i;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        while(top>0&&stack[top]<=a[i])top–;//此处写成 while(top>0&&stack[top]<a[i])top–;
        cnt+=top;
        stack[++top]=a[i];
    }
    printf("%lld",cnt);
    return 0;
}

堆、
st表、
//P3865 【模板】ST表
//https://www.cnblogs.com/Blackops/p/6295094.html此文思路介绍得不错
//样例通过,提交AC 2018-4-11 16:31
#include <stdio.h>
int f[100100][25],n,m,a[100100];
int max(int a,int b){
    return a>b?a:b;
}
int main(){
    int i,j,left,right,k;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&a[i]),f[i][0]=a[i];
    for(j=1;(1<<j)<=n;j++)//此处写成 for(j=1;j<=n;j++)
        for(i=1;i+(1<<j)-1<=n;i++)//此处写成 for(i=1;i<=n;i++)
            f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);//此处写成 f[i][j]=max(f[i][j-1],f[i+(1<<j-1)-1][j-1]);
    while(m–){
        scanf("%d%d",&left,&right),k=0;
        while(1<<k+1<right-left+2)k++;//计算 left+2^k-1<right-(1<<k)+1
        printf("%d\n",max(f[left][k],f[right-(1<<k)+1][k]));
    }
    return 0;
}

//P2251 质量检测
//st表
//样例通过,提交,全WA
//排查,发现将right=m写成了right=4
//修改,提交AC 2018-4-12
#include <stdio.h>
#define maxn 100100
int f[maxn][25],a[maxn];
int min(int a,int b){
    return a<b?a:b;
}
int main(){
    int n,m,i,j,left,right,k;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&a[i]),f[i][0]=a[i];
    for(j=1;(1<<j-1)<=n;j++)
        for(i=1;i+(1<<j-1)<=n;i++)
            f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
    for(left=1,right=m;right<=n;i++){//此处写成 for(left=1,right=4;right<=n;i++) 昏招
        k=0;
        while((1<<k+1)<right-left+2)k++;
        printf("%d\n",min(f[left][k],f[right-(1<<k)+1][k]));
        left++,right++;
    }
    return 0;
}

hash表(哈希表)
水题 hdu 1128 Self Numbers
https://vjudge.net/problem/HDU-1128可见该题
https://blog.csdn.net/laojiu_/article/details/51169445此文代码写得不错
//hdu 1128 Self Numbers
//看了数据范围less than or equal 1000000至少得采用O(n)算法
//https://blog.csdn.net/laojiu_/article/details/51169445此文代码写得不错
//样例通过,提交AC 2018-3-28 17:20
#include <stdio.h>
#include <string.h>
#define maxn 1000100
int hash[maxn];
int fun(int x){
    int sum=0;
    while(x)sum+=x%10,x/=10;
    return sum;
}
int main(){
    int i,t;
    memset(hash,0,sizeof(hash));
    for(i=1;i<=maxn;i++)t=i,t+=fun(i),hash[t]=1;
    for(i=1;i<=maxn-100;i++)
        if(!hash[i])
            printf("%d\n",i);
    return 0;
}
//hdu 1014 Uniform Generator
//https://vjudge.net/problem/HDU-1014可见该题
//哈希表水题
//结合文中例子,看懂该题。
//一直在犹豫,算几次才算结束,
//https://blog.csdn.net/laojiu_/article/details/51165229翻看代码,发现上述问题解决
//总结,多模拟,大胆猜想。
//样例通过,提交AC 2018-3-29
#include <stdio.h>
#include <string.h>
int hash[100100];
int main(){
    int step,mod,i,x;
    while(scanf("%d%d",&step,&mod)!=EOF){
        memset(hash,0,sizeof(hash)),x=0;
        for(i=1;i<=mod;i++){
            x=(x+step)%mod;
            if(!hash[x])hash[x]=1;
            else break;
        }
        if(i==mod+1)printf("%10d%10d    Good Choice\n\n",step,mod);
        else printf("%10d%10d    Bad Choice\n\n",step,mod);
    }
    return 0;
}

线段树、
//https://www.luogu.org/problemnew/show/P3372
//洛谷 P3372 【模板】线段树 1
//调了会,样例通过,提交AC 2018-5-8 收获是 对 线段树 理解更深刻了
#include <stdio.h>
#include <string.h>
#define maxn 100100
#define LL long long
LL n,m,lazy[4maxn],ans,max_k=-999999999;
struct node{
    LL left,right,v;
}q[4
maxn];
void build(LL k,LL left,LL right){
    LL mid=(left+right)/2;
    q[k].left=left,q[k].right=right;
    if(q[k].leftq[k].right){
        scanf("%lld",&q[k].v);
        if(max_k<k)max_k=k;//判断最大节点位置
        return ;
    }
    build(2k,left,mid);
    build(2
k+1,mid+1,right);
    q[k].v=q[2k].v+q[2k+1].v;
}
void down(LL k){
    LL v=lazy[k];
    //此处多写了此句q[k].v+=(q[k].right-q[k].left+1)v;
    if(2
k<=max_k){//判断有无越界
        lazy[2k]+=v;
        q[2
k].v+=(q[2k].right-q[2k].left+1)v;//漏了此句
    }
    if(2
k+1<=max_k){//判断有无越界
        lazy[2k+1]+=v;
        q[2
k+1].v+=(q[2k+1].right-q[2k+1].left+1)v;//漏了此句
    }
    lazy[k]=0;
}
void query(LL k,LL left,LL right){
    LL mid=(q[k].left+q[k].right)/2;//此处写成 LL mid=(left+right)/2;
    if(left<=q[k].left&&q[k].right<=right){
        ans+=q[k].v;
        return ;
    }
    if(lazy[k])down(k);
    if(left<=mid)query(2
k,left,right);
    if(right>=mid+1)query(2k+1,left,right);
}
void add(LL k,LL left,LL right,LL v){
    LL mid=(q[k].left+q[k].right)/2;//此处写成 LL mid=(left+right)/2;
    if(left<=q[k].left&&q[k].right<=right){
        q[k].v+=(q[k].right-q[k].left+1)v;
        lazy[k]+=v;//漏了此句
        return ;
    }
    if(lazy[k])down(k);
    if(left<=mid)add(2
k,left,right,v);//此处写成 query(2
k,left,right)昏招
    if(right>=mid+1)add(2k+1,left,right,v);//此处写成 query(2k+1,left,right)昏招
    q[k].v=q[2k].v+q[2k+1].v;
}
int main(){
    LL i,cmd,x,y,v;
    scanf("%lld%lld",&n,&m);
    build(1,1,n);
    memset(lazy,0,sizeof(lazy));
    while(m–){
        scanf("%lld",&cmd);
        if(cmd
2){
            ans=0;
            scanf("%lld%lld",&x,&y);
            query(1,x,y);
            printf("%lld\n",ans);
        }else{//cmd==1
            scanf("%lld%lld%lld",&x,&y,&v);
            add(1,x,y,v);
        }
    }
    return 0;
}

//P3372 【模板】线段树 1
//枚举的方式,猜测70分。
//果然,提交70分,测试点8,9,10 TLE 2018-5-7 18:01
#include <stdio.h>
#define maxn 100100
#define LL long long
LL a[maxn];
int main(){
    LL n,m,i,j,cmd,x,y,k,sum;
    scanf("%lld%lld",&n,&m);
    for(i=1;i<=n;i++)scanf("%lld",&a[i]);
    while(m–){
        scanf("%lld",&cmd);
        if(cmd==1){
            scanf("%lld%lld%lld",&x,&y,&k);
            for(i=x;i<=y;i++)a[i]+=k;
        }else{
            sum=0;
            scanf("%lld%lld",&x,&y);
            for(i=x;i<=y;i++)sum+=a[i];
            printf("%lld\n",sum);
        }
    }
    return 0;
}

树状数组
字典树
*分块
动态规划

连续最大子段和
//P1115 最大子段和

//https://www.luogu.org/problemnew/show/P1115可提交测评
//f[i]表示以a[i]结尾的最大子段和
//样例通过,提交AC 2018-4-24 20:38
#include <stdio.h>
#define maxn 200100
int a[maxn],f[maxn],MAX;
int max(int a,int b){
    return a>b?a:b;
}
int main(){
    int i,n;
    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    MAX=f[1]=a[1];
    for(i=2;i<=n;i++){//算法的时间复杂度O(n)
        f[i]=max(f[i-1]+a[i],a[i]);
        MAX=max(MAX,f[i]);
    }
    printf("%d",MAX);
    return 0;
}

背包DP、树形DP、记忆化搜索、递推
区间DP、序列DP
*DP优化(不涉及斜率优化、四边形不等式等等)
搜索

暴搜(dfs、bfs)
搜索的剪枝
启发式搜索(A*)
迭代加深搜索、* IDA*
*随机化搜索
其他算法

STL的基本使用方法
脑洞的正确使用方法
*KMP
*状态压缩

省选知识点汇总
冲省选的,先把整理的NOIP知识点学扎实,注意一定要学扎实 
加粗是重点,星号是选学 
学无止境,欢迎大家继续补充~

图论

网络流(dinic,SAP,ISAP选一个,费用流写EK就行。*zkw费用流),二分图
点分治,边分治,*动态点分治
树链剖分,动态树,树分块
虚树,*prufer编码
*仙人掌算法
数据结构

带权并查集
Splay(作为平衡树和维护区间),Treap,替罪羊树
线段树(权值线段树),树状数组,*线段树合并
分块,块状链表,*双向链表
凸包
树套树
主席树,可持久化trie,*其它可持久化数据结构
莫队算法,*树上莫队,CDQ分治,整体二分
二维线段树,*KDtree
*舞蹈链,*二进制分组,*左偏树,*超哥线段树,*后缀平衡树,*fhqTreap
字符串相关算法及数据结构

hash(自然溢出,双hash)
kmp,AC自动机,trie
后缀数组
manacher,最小表示法
*后缀自动机,*回文自动机,*后缀树
数学

线性筛,积性函数,容斥原理,莫比乌斯反演
exgcd,费马小定理,Lucas定理,高中排列组合
高斯消元,概率与期望相关
中国剩余定理,BSGS,欧拉定理
矩阵乘法
单纯形法解线性规划
FFT
线性代数(行列式)
*Simpson积分,高中求导与积分
*群论
*生成函数, 多项式类算法
博弈论相关,*密码学,阶,原根
计算几何

向量的点积/叉积,计算几何基础
*二维计算几何相关,*三维计算几何相关
*半平面交,*旋转卡壳,*三角剖分
搜索

A*,记忆化搜索,迭代深搜,双向广搜
模拟退火,爬山算法,*随机增量法
动态规划

基础DP,树形DP,数位DP,状压DP,期望DP,基环树DP,*插头DP
斜率优化,矩乘优化,单调队列优化,倍增优化,*四边形不等式优化
trie图DP,*仙人掌DP
其他算法

构造,乱搞,随机化,三分法,打表,启发式合并
Huffman树,2-sat,*朱刘算法
说真的,计算几何要么全场不会,要么全场AK。所以尽量花时间在别的地方吧。

版权声明:本文为CSDN博主「mrcrack」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/mrcrack/article/details/79584044

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值