OI 知识点整理(超详细的!!!)
啊,其实一点都不详细,有很多都没有整理,因为博客筛选挺麻烦的,我又懒又颓的。。。
刚刚发现链接好像点不进去(>人<;),对不起是我的锅,目前已经修好了。嘤~
\(21-07-27, update\): 虽然咕了很久,但是不是真咕。。。重新整理.ing,目前按难度分成三个大板块,普及-提高-省选,又分了很多小板块。工程量巨大,施工中。
一. 图论(完)
- 并查集
- 二分图
- 连通分量 & 割点和桥
- 最短路
- 生成树
- 次小生成树
- 拓扑排序
- 欧拉回路 & 哈密顿回路
- 曼哈顿距离
- 差分约束
参考链接//我不知道为什么这篇博文不见了,看下面这篇吧
参考链接2//讲解更详细易懂
参考链接3//内容更完整
二 . 树
- Dsu on tree
例题
-
\(Solution\) 咕
\(CODE\)
#include<cstdio> #include<vector> #include<algorithm> using namespace std; const int N = 5e5 + 5; int n,m,t,flag; int ans[N],head[N],son[N],w[N],dep[N],size[N],check[N],l[N],r[N]; char s[N]; struct query { int deep,id; }; vector<int> G[N];//存边 vector<query> q[N];//询问 void add_p(int x) { int y = s[x] - 'a'; check[dep[x]] ^= (1 << y);//利用位运算的特点 } void add_tree(int x) { for(int i = l[x]; i <= r[x]; i ++) add_p(w[i]);//处理以x为根的子树 } void dfs(int x,int d) { size[x] = 1; dep[x] = d; l[x] = ++t;//记录子树起点 w[t] = x;//记录dfs序 for(int i = 0; i < G[x].size(); i ++) { int k = G[x][i]; dfs(k,d + 1); size[x] += size[k];//子树个数 if(size[son[x]] < size[k]) son[x] = k;//重儿子处理 } r[x] = t;//子树终点 } void dfs2(int x) { for(int j = 0; j < G[x].size(); j ++) { int k = G[x][j]; if(k == son[x]) continue; dfs2(k);//轻儿子处理 add_tree(k);//增加一次 } if(son[x]) dfs2(son[x]);//重儿子 for(int j = 0; j < G[x].size(); j ++) { int k = G[x][j]; if(k == son[x]) continue; add_tree(k);//轻儿子的贡献清空 } add_p(x);//当前节点操作 for(int j = 0; j < q[x].size(); j ++) {//当前子树的查询 int h = check[q[x][j].deep];//深度 ans[q[x][j].id] = (h == (h & -h));//神奇的位运算qaq } } int main() { scanf("%d %d",&n,&m); for(int i = 2; i <= n; i ++) { int u; scanf("%d",&u); G[u].push_back(i); } scanf("%s",s + 1); dfs(1,1); for(int i = 1; i <= m; i ++) { int h,v; scanf("%d %d",&h,&v); q[h].push_back((query){v,i});//储存查询 } dfs2(1); for(int i = 1; i <= m; i ++) { if(ans[i]) printf("Yes\n"); else printf("No\n"); }//输出结果 return 0; }
-
左偏树
-
线段树
-
树状数组
-
平衡树
-
树链剖分
-
字典树
-
KMP
三 . 数学
- 组合计数
例题
-
\(Solution :\) 参考链接
\(CODE\)
#include<cstdio> #include<algorithm> using namespace std; #define int long long const int N = 1e6 + 5,MOD = 1e9 + 7; int n,m,k,mul[N],inv[N],f[N],ans; int qpow(int a,int b,int mod) { int res = 1ll; while(b) { if(b & 1) res = res * a % mod; a = a * a % mod; b >>= 1ll; } return res; } void init() { mul[0] = 1ll; for(int i = 1; i <= n; i ++) mul[i] = mul[i - 1] * i % MOD; inv[n] = qpow(mul[n],MOD - 2,MOD); for(int i = n; i >= 1; i --) inv[i - 1] = inv[i] * i % MOD; m = n - k; } int c(int a,int b) { if(a < b) return 0; if(a < MOD && b < MOD) return mul[a] * inv[b] % MOD * inv[a - b] % MOD; return c(a / MOD,b / MOD) * c(a % MOD,b % MOD) % MOD; } signed main() { scanf("%lld %lld",&n,&k); init(); for(int i = 0; i <= m; i ++) { int tmp = qpow(2,m - i,MOD - 1); f[i] = (i & 1 ? -1ll : 1ll) * c(m,i) * (qpow(2,tmp,MOD) - 1) % MOD; ans = (ans + f[i] + MOD) % MOD; } ans = ans * c(n,k) % MOD; printf("%lld",ans); return 0; }
-
博弈论
- SG函数 & nim函数
- 期望
- 概率
- 高斯消元
- 容斥
- 同余方程
\(CODE\)
#include<cstdio>
int a,b,tmp,x,y;
void gcd(int a,int b,int &x,int &y) {
if(b==0) {
x=1;
y=0;
return;
}
gcd(b,a%b,x,y);
int tmp=x;
x=y;
y=tmp-a/b*y;
}
int main() {
scanf("%d %d",&a,&b);
gcd(a,b,x,y);
printf("%d",(x%b+b)%b);
return 0;
}
- 中国剩余定理
CODE
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll tot,n,ans,m = 1,A[105],B[105],mi[105];
void exgcd(ll a,ll b,ll &x,ll &y) {
if(b == 0) {x = 1, y = 0, return;}
exgcd(b,a % b,x,y);
int z = x;
x = y, y = z - y * (a / b);
}
int main() {
scanf("%lld",&n);
for(int i = 1; i <= n; i ++) { scanf("%lld %lld",&A[i],&B[i]), m *= A[i];}
for(int i = 1; i <= n; i ++) {
mi[i] = m / A[i];
ll x = 0, y = 0;
exgcd(mi[i],A[i],x,y);
ans += B[i] * mi[i] * (x < 0 ? x + A[i] : x);
}
printf("%lld",ans % m);
return 0;
}
- 卢卡斯定理
\(CODE\)
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int N = 1e5 + 5;
int t,n,m,f[N],mod;
void init() {
f[0] = 1;
for(int i = 1; i <= mod; i ++) f[i] = f[i - 1] * i % mod;
}
int qpow(int a,int b) {
a %= mod;
int res = 1;
while(b) {
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int c(int n,int m) {
if(n < m) return 0;
return f[n] * qpow(f[m],mod - 2) % mod * qpow(f[n - m],mod - 2) % mod;
}
int luc(int n,int m) {
return !m ? 1 : luc(n / mod,m / mod) * c(n % mod,m % mod) % mod;
}
signed main() {
scanf("%lld",&t);
while(t--) {
scanf("%lld %lld %lld",&n,&m,&mod);
init();
printf("%lld\n",luc(n + m,n));
}
return 0;
}
- 扩展欧几里得
-
傅里叶变换(FFT)
四 . DP
斜率优化
将与 $ i$, \(j\) 都有关系的乘积项作为 $ kx$,
其中与 $i $ 有关的作为 \(k\),与 \(j\) 有关的作为 \(x\)
将只与$ j$有关系的值作为 \(y\)
其余的当做$ b$
例题
-
\(Solution :\) 斜率柿子要\(dp[j]\)和\(dp[k]\)之间大小比较,推成一元二次方程\(y = kx + b\)的形式,剩下的一般用优先队列维护。
\(dp[i]\):前\(i\)个牧场的最小花费
\(a[i]\):第\(i\)个牧场建立控制站的花费
\(b[i]\):第\(i\)个牧场的放养量
\(sum[i]\):前\(i\)个牧场$ dis[i] * b[i] $ 总和
$w[i] \(: 前\)i$个牧场的放养量之和
$
dp[i] = min{dp[j] - w[j] * i + sum[j]} + a[i] - sum[i] + w[i] * i; $$dp[k] - w[k] * i + sum[k] <= dp[j] - w[j] * i + sum[j] $
\(CODE\)
#include<cstdio> #include<algorithm> #define ll long long using namespace std; const int N = 1e6 + 5; int n,q[N]; ll w[N],a[N],dp[N],sum[N]; ll x(int t) {return dp[t] + sum[t];} ll y(int t) {return w[t];} double k(int a,int b) { return 1.0 * (x(a) - x(b)) / (y(a) - y(b)); } int main() { scanf("%d",&n); for(int i = 1; i <= n; i ++) scanf("%d",&a[i]); for(int i = 1; i <= n; i ++) { scanf("%d",&w[i]); sum[i] = sum[i - 1] + w[i] * i; w[i] += w[i - 1]; } int l = 0,r = 0; for(int i = 1; i <= n; i ++) { while(l < r && k(q[l + 1],q[l]) < i) l++; dp[i] = dp[q[l]] + i * (w[i] - w[q[l]]) - sum[i] + sum[q[l]] + a[i]; while(l < r && k(q[r],q[r - 1]) > k(i,q[r])) r --; q[++r] = i; } printf("%lld",dp[n]); return 0; }
-
树形DP
例题
-
\(Solution\) : 重点在于\(dp\)状态定义的转换,不能局限于传统的\((i,j)\)定义,应适当转换。定义\(dp[i,j]\) 为前\(i\)头奶牛,智商和为\(j\)时的最大情商和,将情商作为\(dp\)值,然后就是传统操作。还有一点要注意的就是因为智商存在负数情况,需要加上一个值使全部为正数,但是最后算答案是记得减去。
小优化:在输入时,如果有两样全为复数的,直接舍去(真是令人悲伤),不会对答案产生任何影响。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define rt register int const int N = 405,M = 4e5; int n,cnt,a[N],b[N],dp[2 * M + 5],mx,ans; inline int MAX(int x,int y) { return x < y ? y : x; } inline void read(int &x) { x = 0; char s = getchar(); int f = 1; while(s < '0' || s > '9') {if(s == '-') f = -1; s = getchar();} while(s >= '0' && s <= '9') {x = x * 10 + s - '0', s = getchar();} x *= f; } int main() { read(n); for(rt i = 1; i <= n; i ++) { read(a[++cnt]), read(b[cnt]); if(a[cnt] < 0 && b[cnt] < 0) cnt--; else mx = MAX(mx,mx + a[cnt]); } memset(dp,-0x3f,sizeof(dp)); dp[M] = 0, mx += M; for(rt i = 1; i <= cnt; i ++) { if(a[i] >= 0) { for(rt j = mx; j >= a[i]; j --) dp[j] = MAX(dp[j],dp[j - a[i]] + b[i]); } else { for(rt j = 0; j <= mx + a[i]; j ++) dp[j] = MAX(dp[j],dp[j - a[i]] + b[i]); } } for(rt i = M; i <= mx; i ++) if(dp[i] >= 0) ans = MAX(ans,i + dp[i] - M); printf("%d",ans); return 0; }
-
背包问题
-
数位DP
-
区间DP
-
状压DP
五 . 其他板块
- CDQ分治
- 二分 & 三分
-
莫队
-
分块
-
马拉车
- 位运算
- STL
- 高精
六 . 调试
- 对拍
- 时间 & 空间复杂度
参考链接:参考链接