2023 国庆CSP-J刷题营笔记整合
Day1
枚举与搜索
枚举
例1:洛谷P1008
- 考虑枚举所有三位数,可过,然而时间复杂度高,容易枚举没写不符合题目的答案
- 可找到1:2:3条件来枚举
例2:洛谷P1217
- 枚举a-b中所有回文质数,TLE
- 枚举a-b中所有质数,TLE*2
- 枚举回文数(正解)
枚举回文数:
1. 枚举前一半(总结规律可以发现如果为质数第一位只能为1 3 7 9)
2. 判断位数奇偶
eg:
枚举了前一半137
奇数:137731
偶数:13731
- 记录数量,AC
例3:洛谷P1102
双指针算法
感觉类似莫队,就是不断缩小范围直到最小区间
#include<bits/stdc++.h>
using namespace std;
#define int long long
int ans;
signed main(){
int a[1000050],n;
a[0]=-10000000;
int vr,vl;
cin>>n>>vl;
vr=vl;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
int j1=0,j0=0;
for(int i=1;i<=n;i++){
while(a[i]-a[j0]>vr) j0++;
while(a[i]-a[j1]>=vl) j1++;
if(j0<j1){
ans+=j1-j0;
//cout<<j1<<' '<<j0<<"\n";
}
}
cout<<ans;
return 0;
}
/*
4 1 1
1 1 2 3
*/
搜索
搜索本质也是枚举,差别在于代码实现
全排列模型
- DFS枚举全排列
STL大法好algorithm库的next_permutation(p+1,p+n+1)
函数,且当p为字典序最大的排列时,它会返回0,否则会返回1
两种方法各有优劣,前者方便减枝,后者实现更为方便。
例1:洛谷P1219
- 枚举全排列,结束
二进制模型、集合/正整数划分模型.......
剪枝
- 变换搜索顺序洛谷P1074
- 可行性剪枝
- 物品排序后搜索(没有什么道理,却能使搜索变得十分优秀。)
- 最优性剪枝(01背包问题,但是DFS)
Day2
数据结构
链表:
单向链表:
前项星:
本质:前项星是由多个单项链表组成,维护链头数组,然后可以支持每个点加边。
struct nod
{
int next,to;
}e[xx*2];
int cnt,h[xx];
void add(int x,int y){
cnt++;
e[cnt]={h[×],y};
h[×]=cnt;
}
void dfs(int x,int y){
for(int i=h[×];i;i=e[i].next)
dfs(e[i].to,x);
}
双向链表:
就比单向链表多了个后继而已=_=
栈
之所以重要,是因为它其实就是递归的逻辑结构,先进后出
STL:
stack<int>stk;
stk.push(1);
stk.pop();
cout<<stk.top()<<"\n";
cout<<stk.size()<<"\n";
单调栈
- 始终保持站内元素为单调的
- 比较下标优先
输入序列:1 4 2 3 5
栈内变化:
5
5 3
5 3 2
5 4
5 4 1
用来求第一个大于某个元素的下标
动态数组vector
顾名思义,它的容量是变化的,是动态的数组
当内部使用的容量到达了它的上界,他的容量就会翻2倍。
如:
a={} a.push_back(1) 0
a={1} a.push_back(2) 1
a={1,2} a.push_back(3) 2
a={1,2,3} a.push_back(4) 4
a={1,2,3,4} a.push_back(5) 8
a={1,2,3,4,5} 16
... ...
我们也可以分配内存a.resize()
也可以释放内存vector<int>().swap(a)
,默认是占24字节
队列
就是模拟现实中的排队的队列,先进先出
双端队列(deque)
就是两个头相对的队列合一块,STL是支持随机访问
然而空间是特别大的,默认分配内存有80个字节,1e6个内存总共80多MB
单调队列
原理跟单调栈类似,就是可以取队首元素可pop队首了
堆
一种完全二叉树,可以维护一个最小值或最大值,插入数值时间复杂度为
对顶堆
一个序列,我们每次加入一个元素,或者进行询问。
维护一个初始指针i=0,每次询问的时候将i=i+1然后询问第i小的值是多少。
哈夫曼树
- 每个数贡献的次数是他到根的边数。
- 数大的贡献较少,即经过的边数较少。
- 如果把边顺次标号为 0,1,则每个叶子到根的一个字符串称为哈夫曼编
码。
Day3
动态规划
简单DP
1.正着递推
- 可以从顶往下推
f[i][j]=max(f[i-1][j],f[i-1][j-1])+a[i][j];
2.倒着递推
- 从下往上推
f[i][j]=max(f[i+1][j],f[i+1][j+1])+a[i][j];
背包DP
给定n个价值为
采药
区间DP
- f[l][r]表示还剩下[l,r]的数,我们先手获得的最大价值==后手获得的最大价值
- 可以直接深搜
Day4
图论
概念
树:
- 无环
- 连通
- U,V之间唯一一条路
有向无环图(DAG):
- 可以进行拓扑排序
- 可以进行DP
- 都为二分图
树
树的储存:
- 记录每个点的父亲
- 每个点存他的儿子
- 当成图来存
树的名词:
- 子树
- 深度
- 路径
- 直径:距离最大的一对点之间的距离为多少
- 重心:以一个点为根,左子树的点和右子树的相等则称该点为一棵树的重心
二叉树
- 每个点最多有两个儿子
- 二叉树的先序遍历为它的DFS顺序
完美二叉树:每个点都有两个儿子
完全二叉树:点n的父亲为n/2,点的儿子为2n、2n+1
DAG最短/长路
直接BFS即可
序
序:
- 传递性(a>=b,b>=c,a>=c)
- 自反性(a<=b,b<=a,a=b)
- 反对称性(a+b=c,c+b≠a)
正序:所有序都可比较
偏序:不是所有序都可比较,但是满足序的三个性质
DAG所有点默认都是偏序
二分图
染色(把一个图染成黑白两色,让着图边两端颜色都不同):只需BFS一遍即可
Day5
贪心
就是局部最优解,看哪个最好选哪个
正确性证明:
- 数学归纳法
例1:
- 定义一个函数f[i]表示取i个线段,最右端最小是多少
- 变换过程中最优解不变
例2:
- f[i]:1~i时间前有多少教师
- g[i]:1~i时间最多的同时讲座的数量
h[i]=i
时间同时讲座的数量f[0]=0=g[0]
- 往i区间里放,
f[i-1]=g[i-1]>=h[i-1]
- 能放得进去,
f[i-1]=f[i],g[i-1]=g[i]>h[i]
。- ∴f[n]=g[n]
2.归纳法
3.交换论证
- 任何最优解都可以可以变成贪心的最优解,
- 变换过程中最优解不变
例题:国王游戏
数学
矩阵
矩阵乘法
省略
矩阵加速递推
- f1=f2=0
- 求
高斯消元
特点:
- 两方程互换,解不变;
- 一方程乘以非零数 k,解不变;
- 一方程乘以数 k 加上另一方程,解不变。
例:
- 7 8 9==13 → 1 0 04
- 4 5 6==12 → 0 1 05
- 1 2 3==11 → 0 0 16
取模(mod)
逆元
定义:
- 假设p是一个质数,且
, - b类似于a的倒数?
- 也就是说,乘上—个b和除以一个a的效果是一样的b称作a的逆元,记作
求:
- 费马小定理
- 如果p为质数,且(a,p)=1;
- 则
其他
埃拉托斯特尼筛法
- 求1~n所有素数
O(nlogn)
- 从小到大枚举数把他的倍数筛去
卡特兰数:
1,1,2,5,14
公式:
- 只要让你输出单个数字的题,暴力程序发现前几个结果为上面的数列
- 可以试着直接输出卡特兰数。
Day6
分治
- 分而治之,就是把一个复杂问题分解为多个简单的子问题来解决
- 限制为复杂问题要可分
应用:
归并排序:
基本流程:
sort(l,r);
mid
sort(l,mid);
sort(mid+1,r);
marge(l,mid,r);
逆序对
基本流程:
solve(l,r);
mid
ans+=solve(l,mid);
ans+=solve(mid+1,r);
void solve:
a b
a[i]>b[j]
区间max之和、最大字段和....
时间复杂度:
最大字段和:T(n)=2T(n/2)+O(n)=O(nlogn)
区间max之和:T(n)=2T(n/2)+O(1)
例题:
洛谷P1966
二分
二分查找
简介:
- 一种查找数组元素的方法
- 数组必须有序
- 时间复杂度O(log(n))
STL
- lower_bound:找到数组中第一个大于等于x的下标,返回一个地址
- upper_bound:用法同lower_bound
- binary_search:二分查找一个元素,返回bool值
板子:洛谷P2249
例题:洛谷P1102
-
可以使用第一天的双指针算法
-
排序后也可以二分查找
二分答案
简介:
-
对答案进行二分查找
-
要有一个明确的(答案)界限,如
2 1 3 5 6
可以二分(界限为4)
例题:洛谷P1873
洛谷P2440
洛谷P1024
洛谷P2678
while(l<=r){
ull mid=(r+l)/2;//l+(r-l)/2或l+r>>1
if(check(mid)){
l=mid+1;
}else{
r=mid-1;
}
}
cout<<r;
分数规划
简介:
-
每种物品有两个权值 a 和 b,选出若干个物品使得
最小/最大。 -
假设我们要求最大值。二分一个答案 mid,然后推式子(为了方便少写了上下界):
-
-
那么只要求出不等号左边的式子的最大值就行了。如果最大值比 0 要大,说明 mid 是可行的,否则不可行。
-
求最小值的方法和求最大值的方法类似.
倍增
树上祖先:
- 设
f[i][j]
为i往上跳 步 - ∴
f[i][j]=f[f[i][j-1]][j-1]
- (↑的意思:跳
步,需要跳 步)
上面就是倍增的思想
LCA
-
求树上两个点最近的公共祖先
-
暴力一步一跳到自己一级祖先容易超时
-
可以用倍增思想来跳
作者:d
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】