啊,其實一點都不詳細,有很多都沒有整理,因為博客篩選挺麻煩的,我又懶又頹的。。。
剛剛發現鏈接好像點不進去(>人<;),對不起是我的鍋,目前已經修好了。嚶~
\(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
- 高精
六 . 調試
- 對拍
- 時間 & 空間復雜度
參考鏈接:參考鏈接