OI 知識點整理(超詳細的!!!)


啊,其實一點都不詳細,有很多都沒有整理,因為博客篩選挺麻煩的,我又懶又頹的。。。

剛剛發現鏈接好像點不進去(>人<;),對不起是我的鍋,目前已經修好了。嚶~

\(21-07-27, update\): 雖然咕了很久,但是不是真咕。。。重新整理.ing,目前按難度分成三個大板塊,普及-提高-省選,又分了很多小板塊。工程量巨大,施工中。

一. 圖論(完)

  • 並查集

刷題鏈接

  • 二分圖

參考鏈接

  • 連通分量 & 割點和橋

參考鏈接

  • 最短路

參考鏈接

  • 生成樹

參考鏈接

  • 次小生成樹

次小生成樹詳解及模板

  • 拓撲排序

參考鏈接

  • 歐拉回路 & 哈密頓回路

參考鏈接

  • 曼哈頓距離

\[dis = | x1 - x2 | + | y1 - y2 | \]

  • 差分約束

參考鏈接//我不知道為什么這篇博文不見了,看下面這篇吧

參考鏈接2//講解更詳細易懂

參考鏈接3//內容更完整

二 . 樹

  • Dsu on tree

參考鏈接

例題

  • Tree Requests

    \(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;
}
  • 擴展歐幾里得

參考鏈接

四 . DP

斜率優化

將與 $ i$, \(j\) 都有關系的乘積項作為 $ kx$,

其中與 $i $ 有關的作為 \(k\),與 \(j\) 有關的作為 \(x\)

將只與$ j$有關系的值作為 \(y\)

其余的當做$ b$

例題

  • 小P的牧場

    \(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

題單推薦

例題

  • \(Cow Exhibition G\)

    \(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

參考鏈接

  • 高精

參考鏈接

六 . 調試

  • 對拍

參考鏈接

  • 時間 & 空間復雜度

參考鏈接:參考鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM