NOIP 信息学 奥赛 考纲 考点 模板 裸题 水题
部分内容转自:http://blog.youkuaiyun.com/txl199106/article/details/71504478
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+1000*2+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.youkuaiyun.com/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.youkuaiyun.com/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.youkuaiyun.com/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[maxn*2];//此处写成 e[maxn*maxn/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.youkuaiyun.com/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.youkuaiyun.com/zxf654073270/article/details/42712351此文代码写得不错
//https://blog.youkuaiyun.com/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.4*10^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.4*10^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.youkuaiyun.com/alongela/article/details/8229702该文思路介绍得不错,思路摘抄如下:
//从左到右依次读取当前牛的高度,从栈顶开始把高度小于或等于当前牛的高度的那些元素删除,此时栈中剩下的元素的数量就是可以看见当前牛的其他牛的数量。把这个数量加在一起,就可以得到最后的答案了。
//仔细想想,这个思路,不通过专门训练,很难想到
//样例通过,提交Wrong Answer
//仔细思考,(80000+0)/2*80000=32*10^8=3.2*10^9 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.youkuaiyun.com/laojiu_/article/details/51169445此文代码写得不错
- //hdu 1128 Self Numbers
//看了数据范围less than or equal 1000000至少得采用O(n)算法
//https://blog.youkuaiyun.com/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.youkuaiyun.com/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[4*maxn],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].left==q[k].right){
scanf("%lld",&q[k].v);
if(max_k<k)max_k=k;//判断最大节点位置
return ;
}
build(2*k,left,mid);
build(2*k+1,mid+1,right);
q[k].v=q[2*k].v+q[2*k+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[2*k]+=v;
q[2*k].v+=(q[2*k].right-q[2*k].left+1)*v;//漏了此句
}
if(2*k+1<=max_k){//判断有无越界
lazy[2*k+1]+=v;
q[2*k+1].v+=(q[2*k+1].right-q[2*k+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(2*k+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(2*k+1,left,right,v);//此处写成 query(2*k+1,left,right)昏招
q[k].v=q[2*k].v+q[2*k+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。所以尽量花时间在别的地方吧。