【索引】写得好的博客或者部分算法模板
常用
大括号的markdown
$$\left\{
\begin{aligned}
...\\
...\\
\end{aligned}
\right.
$$
手动O(2)优化
#pragma GCC optimize(2)
对拍程序
先🐎一份数据生成程序
以a+b为例
#include <bits/stdc++.h>
using namespace std;
int main() {
srand( time( 0 ) );
freopen( "data.in", "w", stdout );//将生成数据存入data.in中
int a = rand() % 10000, b = rand() % 10000;
printf( "%d %d", a, b );
return 0;
}
然后是自己的正解 代码
#include <cstdio>
int main() {
freopen( "data.in", "r", stdin );//从data.in中读入数据
freopen( "ans.out", "w", stdout );//将程序运行结果保存在ans.out中
int a, b;
scanf( "%d %d", &a, &b );
printf( "%d", a + b );
return 0;
}
接着就是暴力代码 一定要保证暴力是正确的
#include <cstdio>
int main() {
freopen( "data.in", "r", stdin );//从data.in中读入数据
freopen( "baoli.out", "w", stdout );//将程序运行结果保存在baoli.out中
int a, b;
scanf( "%d %d", &a, &b );
while( b -- ) a ++;
printf( "%d", a );
return 0;
}
准备对拍神器
//新建一个txt文件,先把下面的内容敲进去,然后把文件扩展名(.txt)改成(.bat)
:loop
data.exe //先运行data生成数据
ans.exe //自己的程序运行
baoli.exe //暴力运行
fc ans.out baoli.out //两个程序的结果比较
if errorlevel 1 pause //如果不一样就会暂停
goto loop //一样就继续对拍
最后就可以愉快的对拍了
如果拍出问题就会👇,然后就可以手玩数据
数据在手天下我有
如果没有问题,就会一直循环拍下去,👇
所以在考场中,如果你有时间写对拍,就可以写了过后让它自己拍着,先往下做
说不定什么时候回头看一眼,你就拍出问题来了
dalao肯定都是一遍敲过的~~
随机化
#include <ctime>
#include <algorithm>
using namespace std;
int main() {
srand( time( 0 ) );
int n = rand();
}
有些毒瘤题的正解会有将这个数组随机化打乱,避免出题人故意构造卡人,然后大家都能\(AC\)
如果\(WA\)了,不是代码问题,建议自行购买彩票
#include <algorithm>
using namespace std;
int main() {
random_shuffle( a + 1, a + n + 1 );
//由计算机来执行将a数组从1~n的数据随机打乱
return 0;
}
交互
printf();
fflush( stdout );
scanf();
代码
O(n)求树的重心
树的重心:最大儿子的节点数最小(每一个儿子的节点数不超过总节点数的一半)
int root, maxx = 0x3f3f3f3f;
void dfs( int u, int fa ) {
siz[u] = 1;
int MaxSon = 0;
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i];
if( v == fa ) continue;
dfs( v, u );
siz[u] += siz[v];
MaxSon = max( MaxSon, siz[v] );
}
MaxSon = max( MaxSon, n - siz[u] );
if( MaxSon < maxx ) {
maxx = MaxSon;
root = u;
}
}
二分图
给定一个二分图\(G\),在\(G\)的一个子图\(M\)中, \(M\)的边集\(\{E\}\)中的任意两条边都不交汇于同一个结点,则称\(M\)是一个匹配
选择边数最大的子图称为最大匹配问题
如果一个匹配中,图的每个顶点都和图中某条边相关联,则称此匹配为完全(完美/完备)匹配
最小边覆盖=最大独立集=总节点数-最大匹配数
交叉染色法判断二分图
bool flag = 1;
memset( vis, -1, sizeof( vis ) );
void dfs( int u, int color ) {
vis[u] = color;
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i];
if( vis[v] == -1 ) dfs( v, color ^ 1 );
else if( vis[u] == vis[v] ) {
flag = 0;
return;
}
}
}
匈牙利算法求二分图最大匹配
时间复杂度:邻接矩阵最坏\(O(n^3)\),邻接表\(O(nm)\)
空间复杂度:邻接矩阵\(O(n*m)\),邻接表\(O(n+m)\)
邻接表无论是时间还是空间上都不会劣于邻接矩阵
都0202年了不会还有人在用邻接矩阵吧ε=ε=ε=┏(゜ロ゜;)┛
//洛谷上面模板题:P3386
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
#define maxn 505
vector < int > G[maxn];
int n, m, e, ans;
bool used[maxn];
int belong[maxn];
bool find( int u ) {
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i];
if( used[v] ) continue;
else {
used[v] = 1;
if( ! belong[v] || find( belong[v] ) ) {
belong[v] = u;
return 1;
}
}
}
return 0;
}
int main() {
scanf( "%d %d %d", &n, &m, &e );
for( int i = 1, u, v;i <= e;i ++ ) {
scanf( "%d %d", &u, &v );
G[u].push_back( v );
}
for( int i = 1;i <= n;i ++ ) {
memset( used, 0, sizeof( used ) );
if( find( i ) ) ans ++;
}
printf( "%d\n", ans );
return 0;
}
KM算法求二分图最大权完美匹配
模拟KM算法的良心博客,b( ̄▽ ̄)d
hdu卡的不是1,是我
//题目:hdu 2255
//但稍微改改是过不了洛谷上的板题的
//这个好像实际上是O(n^4)
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 305
#define inf 0x3f3f3f3f
int n;
int love[maxn][maxn];
int ex_girl[maxn];
int ex_boy[maxn];
bool vis_girl[maxn];
bool vis_boy[maxn];
int match[maxn];
int need[maxn];
bool find( int girl ) {
vis_girl[girl] = 1;
for( int boy = 1;boy <= n;boy ++ ) {
if( vis_boy[boy] ) continue;
int gap = ex_girl[girl] + ex_boy[boy] - love[girl][boy];
if( ! gap ) {
vis_boy[boy] = 1;
if( ! match[boy] || find( match[boy] ) ) {
match[boy] = girl;
return 1;
}
}
else need[boy] = min( need[boy], gap );
}
return 0;
}
int KM() {
memset( match, 0, sizeof( match ) );
memset( ex_boy, 0, sizeof( ex_boy ) );
for( int i = 1;i <= n;i ++ ) {
ex_girl[i] = love[i][1];
for( int j = 2;j <= n;j ++ )
ex_girl[i] = max( ex_girl[i], love[i][j] );
}
for( int i = 1;i <= n;i ++ ) {
memset( need, 0x3f, sizeof( need ) );
while( 1 ) {
memset( vis_girl, 0, sizeof( vis_girl ) );
memset( vis_boy, 0, sizeof( vis_boy ) );
if( find( i ) ) break;
int d = inf;
for( int j = 1;j <= n;j ++ )
if( ! vis_boy[j] ) d = min( d, need[j] );
for( int j = 1;j <= n;j ++ ) {
if( vis_girl[j] ) ex_girl[j] -= d;
if( vis_boy[j] ) ex_boy[j] += d;
else need[j] -= d;
}
}
}
int ans = 0;
for( int i = 1;i <= n;i ++ )
ans += love[match[i]][i];
return ans;
}
int main() {
while( ~ scanf( "%d", &n ) ) {
for( int i = 1;i <= n;i ++ )
for( int j = 1;j <= n;j ++ )
scanf( "%d", &love[i][j] );
printf( "%d\n", KM() );
}
return 0;
}
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
#define int long long
#define INF 1e16
#define maxn 505
vector < pair < int, int > > G[maxn];
int n, m;
int lx[maxn], ly[maxn], slack[maxn], match[maxn];
bool vis[maxn];
void explore( int x ) {
for( int i = 0;i < G[x].size();i ++ ) {
int y = G[x][i].first;
slack[y] = min( slack[y], lx[x] + ly[y] - G[x][i].second );
}
}
bool find( int x ) {
for( int i = 0;i < G[x].size();i ++ ) {
int y = G[x][i].first;
if( lx[x] + ly[y] == G[x][i].second && ! vis[y] ) {
vis[y] = 1;
if( ! match[y] || find( match[y] ) ) {
match[y] = x;
return 1;
}
}
}
return 0;
}
signed main() {
scanf( "%lld %lld", &n, &m );
for( int i = 1, u, v, w;i <= m;i ++ ) {
scanf( "%lld %lld %lld", &u, &v, &w );
G[u].push_back( make_pair( v, w ) );
}
for( int i = 1;i <= n;i ++ ) {
lx[i] = -INF, ly[i] = 0;
for( int j = 0;j < G[i].size();j ++ )
lx[i] = max( lx[i], G[i][j].second );
}
for( int i = 1;i <= n;i ++ ) {
for( int j = 1;j <= n;j ++ )
vis[j] = 0, slack[j] = INF;
explore( i );
while( 1 ) {
int d = INF, pos;
for( int j = 1;j <= n;j ++ )
if( ! vis[j] && slack[j] < d )
d = slack[j], pos = j;
if( ! d ) {
vis[pos] = 1;
if( ! match[pos] ) {
/*
如果不清空
匈牙利里面的判断!vis[j]就变成vis[j]
因为我之前肯定是跑过这个点的
这样比较暴力
时间复杂度O(nm+n^3)
*/
memset( vis, 0, sizeof( vis ) );
find( i );
//match[pos]=i;
/*
这种写法是错误的
因为pos和i不一定能直接配对
所以需要像匈牙利一条一条改变配对
*/
break;
}
else explore( match[pos] );
}
else {
for( int j = 1;j <= n;j ++ )
if( vis[j] ) {
ly[j] += d;
lx[match[j]] -= d;
}
else slack[j] -= d;
lx[i] -= d;
}
}
}
int ans = 0;
for( int i = 1;i <= n;i ++ )
ans += lx[i] + ly[i];
printf( "%lld\n", ans );
for( int i = 1;i <= n;i ++ )
printf( "%lld ", match[i] );
return 0;
}
二维树状数组
int lowbit( int x ) {
return x & ( -x );
}
void add( int x, int y ) {
for( int i = x;i <= n;i += lowbit( i ) )
for( int j = y;j <= n;j += lowbit( j ) )
tree[i][j] ++;
}
int query( int x, int y ) {
int ans = 0;
for( int i = x;i;i -= lowbit( i ) )
for( int j = y;j;j -= lowbit( j ) )
ans += tree[i][j];
return ( ans & 1 );
}
scanf( "%d %d %d %d", &x1, &y1, &x2, &y2 );
add( x1, y1 );
add( x2 + 1, y1 );
add( x1, y2 + 1 );
add( x2 + 1, y2 + 1 );
点分治
//poj1741
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 10005
#define inf 1e9
vector < pair < int, int > > G[maxn];
int n, k, cnt, maxx, root, S, ans;
bool vis[maxn];
int siz[maxn], dis[maxn];
void dfs( int u, int fa ) {
siz[u] = 1;
int MaxSon = 0;
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i].first;
if( v == fa || vis[v] ) continue;
dfs( v, u );
siz[u] += siz[v];
MaxSon = max( MaxSon, siz[u] );
}
MaxSon = max( MaxSon, S - siz[u] );
if( MaxSon < maxx )
maxx = MaxSon, root = u;
}
void getdis( int u, int d, int fa ) {
dis[++ cnt] = d;
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i].first, w = G[u][i].second;
if( vis[v] || v == fa ) continue;
else getdis( v, d + w, u );
}
}
int calc( int u, int w ) {
cnt = 0;
getdis( u, w, 0 );
sort( dis + 1, dis + cnt + 1 );
int l = 1, r = cnt, tot = 0;
while( l <= r )
dis[l] + dis[r] <= k ? tot += r - l, l ++ : r --;
//如果条件成立 说明l和(l,r]的每一个点的距离都≤k
return tot;
}
void solve( int u ) {
vis[u] = 1;
ans += calc( u, 0 );
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i].first, w = G[u][i].second;
if( vis[v] ) continue;
ans -= calc( v, w );
maxx = inf, S = siz[v]; //往下分治 以儿子v为一棵新树的根 所有信息全部更新
dfs( v, u );
solve( root ); //新树的重心分治
}
}
int main() {
while( scanf( "%d %d", &n, &k ) ) {
if( ! n && ! k ) return 0;
for( int i = 1;i <= n;i ++ )
G[i].clear(), vis[i] = 0;
for( int i = 1, u, v, w;i < n;i ++ ) {
scanf( "%d %d %d", &u, &v, &w );
G[u].push_back( make_pair( v, w ) );
G[v].push_back( make_pair( u, w ) );
}
maxx = inf, S = n;
dfs( 1, 0 );
ans = 0;
solve( root );
printf( "%d\n", ans );
}
return 0;
}
优质恰饭
这个知乎博主真的写得好!
行列式的本质是什么?
如何理解矩阵的「秩」?
矩阵和向量组和线性方程组之间的关系是什么?
如何通俗地解释泰勒公式?
牛顿插值的几何解释是怎么样的?
如何直观地理解拉格朗日插值法?
又是一个NB知乎博主
怎样更好地理解并记忆泰勒展开式?
Hall定理
hall定理就是判定二分图是否存在完美匹配
一个推论:假设两边的点集分别为\(X,Y\)
则二分图的最大匹配数为\(|X|−max{|W|−|N(W)|}\)
其中\(W\)是\(X\)的子集,\(N(W)\)为\(W\)的所有相邻点
对于一些特殊的题目,利用该推论,可以免去建图而直接求最大匹配
生成函数
对于序列\(a_i\),生成函数为\(G=\sum_{i=0}^∞a_ix^i\)
利用生成函数的性质解决一些组合问题
可供练习的题目应用
单位根反演
在\(O(k)\)的时间内求一个数列(或是生成函数)所有下标是\(k\)的倍数的点值和
有些时候只知道\(k|n\)的点值和还不够,比如求下标\(mod\ k=r\)的点值和
考虑通过函数的平移来解决问题
将该序列的生成函数乘上\(x^{-r}\)
再套用上面的方法就可以得到答案了
你谷日报的单位根反演
浅谈“不动点”求数列通项的方法
b站微积分中文配音讲解
分治FFT 柿子推导过程很清晰
分块详解(▭-▭)✧
数学推导
catlan数的普通生成函数
问:求卡特兰数\(c_0=1,c_n=\sum_{i=0}^{n-1}c_ic_{n-i-1}\)的普通生成函数
令\(c_n\)的生成函数为\(C(x)\)
将\(x^n\)拆分成\(x^ix^{n-i-1}x\),再令\(j=n-i-1\),并进行顺序调整
诶??\(C(x)=C^2(x)x\),当\(C(x)=0\)也成立,是不是哪里出了点问题
的确!其实初始转换时应该有下标的差异,但是最后变成\(i,j\)后是看不出来的
所以需要修正一下
发现不等号的右边比设置的\(C(x)\)就差了\(n=0\)这一项,即\(c_0x^0=1\),所以加上
真正的推导应该是
解得\(C(x)=\frac{1±\sqrt{1-4x}}{2x}\)