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

参考链接

  • 高精

参考链接

六 . 调试

  • 对拍

参考链接

  • 时间 & 空间复杂度

参考链接:参考链接

posted @ 2021-01-16 16:23  Spring-Araki  阅读(817)  评论(0编辑  收藏  举报