Celoria的板子(last update:20201017)

数据结构

树状数组

基础
struct BIT
{
	ll c[MAXN];
	void clear() {mst(c, 0);}
	int lowbit(int x) {return x & (-x);} //最低一位1
	void update(int x, ll t) {for(; x < MAXN; x += lowbit(x)) c[x] += t;} 
	ll query(ll x) 
	{
		ll res = 0; 
		for(; x; x -= lowbit(x)) res += c[x]; 
		return res;
	}
}bit;
二维树状数组
struct TWO_DBIT
{
	static const int MAXN = 333;
	ll c[MAXN][MAXN];
	void init() {mst(c, 0);}
	int lowbit(int x) {return x & (-x);}
	void update(int x, int y, ll k, int N, int M) 
	{
		for(int i = x; i <= N; i += lowbit(i))
			for(int j = y; j <= M; j += lowbit(j)) c[i][j] += k;
	}
	ll query(int x, int y)
	{
		ll res = 0;
		for(int i = x; i; i -= lowbit(i)) 
			for(int j = y; j; j -= lowbit(j)) res += c[i][j];
		return res;
	}
};
逆序对

可处理元素重复的情况,update范围在 [ x , n ] [x,n] [x,n]

struct SF 
{   
    int pos, num;
    bool operator < (const SF& a) const 
    {
        if(a.num==num) return a.pos>pos;
        return a.num < num;
    }
}a[MAXN];
ll cal()
{
    ll res = 0; bit.clear(); 
    rep(i, 1, n + 1) a[i].num = b[i], a[i].pos = i; 
    sort(a + 1, a + n + 1);
    ll cnt = 1;
    rep(i, 1, n + 1){
        res += bit.query(a[i].pos - 1);
        bit.update(a[i].pos, 1);
        if(a[i].num == a[i - 1].num) cnt++;
    } return res;
}

线段树

#define ls (x << 1)
#define rs (x << 1 | 1)
const int MAXN = 2e6 + 11, INF = 1e6;
int a[MAXN], b[MAXN]; ll ans;
struct SF {int l, r; ll sum, mx;}seg[MAXN << 2];
void build(int x, int l, int r)
{
	if(l == r) {seg[x].mx = l * 1ll; return;}
	int mid = (l + r) >> 1;
	build(ls, l, mid); build(rs, mid + 1, r); 
	seg[x].sum = seg[ls].sum + seg[rs].sum;
	seg[x].mx = max(seg[rs].sum + seg[ls].mx, seg[rs].mx);
}
void update(int x, int l, int r, int t, ll d)
{
	if(l == r)
	{
		seg[x].mx += d, seg[x].sum += d;
		return;
	}
	int mid = (l + r) >> 1;
	if(t <= mid) update(ls, l, mid, t, d); 
	else update(rs, mid + 1, r, t, d);
	seg[x].sum = seg[ls].sum + seg[rs].sum;
	seg[x].mx = max(seg[rs].sum + seg[ls].mx, seg[rs].mx);
}
ll query(int x, int l, int r, int t)
{
	if(r <= t) return ans = max(seg[x].sum + ans, seg[x].mx);
	int mid = (l + r) >> 1;
	query(ls, l, mid, t);
	if(t > mid) query(rs, mid + 1, r, t);
	return ans;
}

CDQ分治

P3810 三维偏序

int n, k, cnt; 
const int MAXN = 2e5 + 11;
int c[MAXN], ans[MAXN];
struct SF
{
	int a, b, c, num, f;
}tmp[MAXN], nod[MAXN];
int lowbit(int x) {return x & (-x);}
bool cmp(SF x, SF y)
{
	if(x.a != y.a) return x.a < y.a;
	if(x.b != y.b) return x.b < y.b;
	return x.c < y.c;
}
void update(int x, int t)
{
	for(; x <= k; x += lowbit(x)) c[x] += t;
}
int query(int x)
{
	int res = 0;
	for(; x; x -= lowbit(x)) res += c[x];
	return res;
}
void CDQ(int l, int r)
{
	if(l == r) return;
	int mid = (l + r) >> 1;
	CDQ(l, mid); CDQ(mid + 1, r);
	int p = l, q = mid + 1, tot = l;
	while(p <= mid && q <= r)
	{
		if(nod[p].b <= nod[q].b) update(nod[p].c, nod[p].num), tmp[tot++] = nod[p++];
		else nod[q].f += query(nod[q].c), tmp[tot++] = nod[q++];
	}
	while(p <= mid) update(nod[p].c, nod[p].num), tmp[tot++] = nod[p++];
	while(q <= r) nod[q].f += query(nod[q].c), tmp[tot++] = nod[q++];
	rep(i, l, mid) update(nod[i].c, -nod[i].num); 
	rep(i, l, r) nod[i] = tmp[i];
}
int main()
{
	n = re, k = re; cnt = 1;
	rep(i, 1, n) nod[i].a = re, nod[i].b = re, nod[i].c = re, nod[i].num = 1;
	sort(nod + 1, nod + n + 1, cmp);
	rep(i, 2, n)
	{
		if(nod[i].a == nod[cnt].a && nod[i].b == nod[cnt].b && nod[i].c == nod[cnt].c) nod[cnt].num++;
		else nod[++cnt] = nod[i];
	}
	CDQ(1, cnt); 
	rep(i, 1, cnt) ans[nod[i].f + nod[i].num - 1] += nod[i].num; 
	rep(i, 0, n - 1) printf("%d\n", ans[i]); 
	return 0;
}

树论

树的重心

定义

​ 找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小。换句话说,删除这个点后最大连通块(一定是树)的结点数最小。

性质
  • 一棵树最多有两个重心,且相邻。

  • 删除重心后所得的所有子树,节点数不超过原树的1/2

  • 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。

  • 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的路径上。

  • 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个重心,则他们的距离和相等

代码
void init() {r1 = r2 = 0; rep(i, 0, n + 1) edg[i].clear(), son[i] = 0, siz[i] = 1;}
void dfs(int u, int fa)
{
	for(int v : edg[u])
	{
		if(v == fa) continue;
		dfs(v, u); siz[u] += siz[v];
		son[u] = max(son[u], siz[v]);
	} 
	son[u] = max(son[u], n - siz[u]);
	if((son[u] << 1) <= n) r2 = r1, r1 = u;
}

树的直径

性质
  • 直径两端点一定是两个叶子节点

  • 若一棵树存在多条直径,那么这些直径交于一点且交点是这些直径的中点

  • 对于一棵树,如果在一个点的上接一个叶子节点,那么最多会改变直径的一个端点

  • 距离任意点最远的点一定是直径的一个端点,这个基于贪心求直径方法的正确性可以得出

  • 对于两棵树,如果第一棵树直径两端点为(u,v),第二棵树直径两端点为(x,y),用一条边将两棵树连接,那么新树的直径一定是u,v,x,y中的两个点

代码
void init() {maxl = 0; rep(i, 1, n) dis[i] = 0;}
void dfs(int u, int fa, int fl)
{
	for(int i = hed[u]; i; i = edg[i].nxt)
	{
		int v = edg[i].to;
		if(v == fa) continue;
		dis[v] = dis[u] + edg[i].val; dfs(v, u, fl); 
		if(dis[v] > maxl) maxl = dis[v], d[fl] = v;
	}
}
int work()
{
	n = re; 
	rep(i, 2, n) 
	{
		int x = re, y = re, c = re;
		add_edge(x, y, 1ll * c);
	}
	dis[1] = 0; dfs(1, -1, 0); init();  
	dis[d[0]] = 0; dfs(d[0], -1, 1);
	return printf("%lld\n", dis[d[1]]);
}

LCA

struct LCA
{
	static const int MAXN = 5e5 + 11;
	int rt, cnt; int dep[MAXN], hed[MAXN], fa[MAXN][33];
	struct SF{int to, nxt;}edg[MAXN << 1];
	void add_edge(int x, int y)
	{
		edg[++cnt].to = y; edg[cnt].nxt = hed[x]; hed[x] = cnt;
		edg[++cnt].to = x; edg[cnt].nxt = hed[y]; hed[y] = cnt;
	}
	void init(int N)
	{
		for(int j = 1; (1 << j) <= N; j++) 
			rep(i, 1, N) fa[i][j] = fa[fa[i][j - 1]][j - 1]; 
	}	
	void dfs(int u, int f)
	{
		for(int i = hed[u]; i; i = edg[i].nxt)
		{
			int v = edg[i].to; 
			if(v == f) continue;
			dep[v] = dep[u] + 1; fa[v][0] = u; dfs(v, u);
		}
	}
	int lca(int a, int b)
	{
		if(dep[a] < dep[b]) swap(a, b);
		int tmp = dep[a] - dep[b];
		for(int i = 0; tmp; i++, tmp >>= 1) if(tmp & 1) a = fa[a][i];
		if(a == b) return a;
		per(i, 19, 0) if(fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i];
		return fa[a][0];
	}
};

主席树

vector <int> vec;
int getid(int x) {return lower_bound(vec.begin(), vec.end(), x) - vec.begin() + 1;}
struct SF
{
	int l, r, sum;
}hjt[MAXN * 50];
void insert(int l, int r, int pre, int &now, int p)
{
	hjt[now = ++cnt] = hjt[pre], hjt[now].sum++; 
	if(l == r) return;
	int mid = (l + r) >> 1;
	if(p <= mid) insert(l, mid, hjt[pre].l, hjt[now].l, p); 
	else insert(mid + 1, r, hjt[pre].r, hjt[now].r, p); 
} 
int query(int l, int r, int L, int R, int k)
{ 
	if(l == r) return l;
	int mid = (l + r) >> 1, tmp = hjt[hjt[R].l].sum - hjt[hjt[L].l].sum;
	if(k <= tmp) return query(l, mid, hjt[L].l, hjt[R].l, k);
	else return query(mid + 1, r, hjt[L].r, hjt[R].r, k - tmp);
}
int main()
{
	n = re, m = re;
	rep(i, 1, n) vec.pb(a[i] = re);
	sort(vec.begin(), vec.end()); vec.erase(unique(vec.begin(), vec.end()), vec.end());
    rep(i, 1, n) insert(1, n, rt[i - 1], rt[i], getid(a[i]));
	rep(i, 1, m)
	{
		int l = re, r = re, k = re;
		printf("%d\n", vec[query(1, n, rt[l - 1], rt[r], k) - 1]);
	}
	return 0;
}

平衡树

替罪羊树

支持前驱后继的查询

struct SCAPAGOAT
{
	int cnt, root;
	vector <int> vec;
	struct SF
	{
		int l, r, vlu, tot, fac; bool exi;
	}spg[MAXN]; 
	void newnode(int &id, int w)
	{
		id = ++cnt, spg[id].fac = spg[id].tot = 1;
		spg[id].exi = 1, spg[id].vlu = w;
	}
	void update(int id, int g)
	{
		if(!id) return;
		if(spg[g].vlu < spg[id].vlu) update(spg[id].l, g);
		else update(spg[id].r, g);
		spg[id].tot = spg[spg[id].l].tot + spg[spg[id].r].tot + 1;
	}
	void ldr(int id)
	{
		if(!id) return;
		ldr(spg[id].l);
		if(spg[id].exi) vec.push_back(id);
		ldr(spg[id].r);
	}
	void lift(int &id, int l, int r)
	{
		if(l == r) 
		{
			id = vec[l]; spg[id].l = spg[id].r = 0;
			spg[id].fac = spg[id].tot = 1;
			return;
		}
		int mid = (l + r) >> 1;
		while(l < mid && spg[vec[mid]].vlu == spg[vec[mid - 1]].vlu) mid--;
		id = vec[mid];
		if(l < mid) lift(spg[id].l, l, mid - 1); 
		else spg[id].l = 0;
		lift(spg[id].r, mid + 1, r);
		spg[id].tot = spg[spg[id].l].tot + spg[spg[id].r].tot + 1;
		spg[id].fac = spg[spg[id].l].fac + spg[spg[id].r].fac + 1;
	}
	void rebuild(int &id)
	{
		vec.clear(); ldr(id);
		if(vec.empty()) {id = 0; return;}
		lift(id, 0, vec.size() - 1);
	}
	bool balence(int id)
	{
		if(max(spg[spg[id].l].tot, spg[spg[id].r].tot) > spg[id].tot * 0.75
			|| (spg[id].tot - spg[id].fac) > spg[id].tot * 0.3) return 0;
		return 1;
	}
	void check(int &id, int g)
	{
		if(id == g) return;
		if(!balence(id)) {rebuild(id); update(root, id); return;}
		if(spg[g].vlu < spg[id].vlu) check(spg[id].l, g); 
		else check(spg[id].r, g);
	}
	void insert(int &id, int w)
	{
		if(!id)
		{
			newnode(id, w);
			check(root, id); return;
		}
		spg[id].fac++, spg[id].tot++;
		if(w < spg[id].vlu) insert(spg[id].l, w);
		else insert(spg[id].r, w);
	}
	void delet(int id, int w)
	{
		if(spg[id].exi && spg[id].vlu == w) 
		{
			spg[id].fac--, spg[id].exi = 0;
			check(root, id); return;	
		} 
		spg[id].fac--;
		if(w < spg[id].vlu) delet(spg[id].l, w);
		else delet(spg[id].r, w);
	}
	int getrank(int w)
	{
		int id = root, res = 1;
		while(id)
		{
			if(w <= spg[id].vlu) id = spg[id].l;
			else 
			{
				res += spg[spg[id].l].fac + spg[id].exi;
				id = spg[id].r;
			}
		}
		return res;
	}
	int getnum(int rk)
	{
		int id = root;
		while(id)
		{
			if(spg[id].exi && spg[spg[id].l].fac + spg[id].exi == rk) break;
			else if(spg[spg[id].l].fac >= rk) id = spg[id].l;
			else rk -= spg[spg[id].l].fac + spg[id].exi, id = spg[id].r;
		}
		return spg[id].vlu;
	}
}spg;
Treap
区间翻转
mt19937 rnd(233); 
const int MAXN = 1e5 + 11;
int n, m, cnt, root, a, b, c;
struct SF
{
	bool rev;
	int l, r, val, key, siz;
}fhq[MAXN];
int newnode(int w) 
{
	fhq[++cnt].val = w, fhq[cnt].key = rnd();
	fhq[cnt].siz = 1; fhq[cnt].rev = 0; return cnt;
}
void update(int now) {fhq[now].siz = fhq[fhq[now].l].siz + fhq[fhq[now].r].siz + 1;}
void pushdown(int now)
{
	swap(fhq[now].l, fhq[now].r);
	fhq[fhq[now].l].rev ^= 1; fhq[fhq[now].r].rev ^= 1;
	fhq[now].rev = 0;
}
void split(int now, int siz, int &x, int &y)
{
	if(!now) {x = y = 0; return;}
	if(fhq[now].rev) pushdown(now);
	if(fhq[fhq[now].l].siz < siz) 
		x = now, split(fhq[now].r, siz - fhq[fhq[now].l].siz - 1, fhq[now].r, y);
	else y = now, split(fhq[now].l, siz, x, fhq[now].l);
	update(now);
}
int merge(int x, int y)
{
	if(!x || !y) return x + y;
	if(fhq[x].key < fhq[y].key)
	{
		if(fhq[x].rev) pushdown(x);
		fhq[x].r = merge(fhq[x].r, y);
		update(x); return x;
	}
	else 
	{
		if(fhq[y].rev) pushdown(y);
		fhq[y].l = merge(x, fhq[y].l);
		update(y); return y;
	}
}
void reverse(int l, int r)
{
	int x, y, z;
	split(root, l - 1, x, y);
	split(y, r - l + 1, y, z);
	fhq[y].rev ^= 1; 
	root = merge(merge(x, y), z);
}
void ldr(int now)
{
	if(!now) return;
	if(fhq[now].rev) pushdown(now);
	ldr(fhq[now].l); printf("%d ", fhq[now].val); ldr(fhq[now].r);
}
int work()
{
	rep(i, 1, n) root = merge(root, newnode(i));
	rep(i, 1, m) 
	{
		int x = re, y = re;
		reverse(x, y);
	}
	ldr(root); return puts("");
}
Splay
下标建树

洛谷P3391 文艺平衡树

struct SPLAY_INDEX
{
	int cnt = 0, rt = 0; 
	const int MAXN = 1e6 + 11;
	struct SF{int son[2], fa, siz, val, flag;}spl[MAXN];
	void update(int now) {spl[now].siz = spl[spl[now].son[0]].siz + spl[spl[now].son[1]].siz + 1;} 
	void UP(int now) {while(now) update(now), now = spl[now].fa;}
	void newnode(int &now, int fa, int val)
	{
		spl[now = ++cnt].val = val; spl[now].fa = fa;
		spl[now].siz = 1; spl[now].son[0] = spl[now].son[1] = spl[now].flag = 0;	
	}
	void build(int &now, int fa, int l, int r)
	{
		int mid = (l + r) >> 1; newnode(now, fa, mid);
		if(l < mid) build(spl[now].son[0], now, l, mid - 1);
		if(r > mid) build(spl[now].son[1], now, mid + 1, r);
		update(now);
	}
	void down(int now)
	{
		if(spl[now].flag)
		{
			spl[now].flag = 0;
			swap(spl[now].son[0], spl[now].son[1]); 
			if(spl[now].son[0]) spl[spl[now].son[0]].flag ^= 1;
			if(spl[now].son[1]) spl[spl[now].son[1]].flag ^= 1;
		}
	}
	bool check(int now, int fa) {return spl[fa].son[1] == now;}
	void connect(int now, int fa, int fl) {spl[fa].son[fl] = now; spl[now].fa = fa;}
	void rotate(int now)
	{
		int f = spl[now].fa, g = spl[f].fa, fl = check(now, f);
		connect(spl[now].son[fl ^ 1], f, fl);
		connect(now, g, check(f, g)); connect(f, now, fl ^ 1);
		update(f); update(now);	
	}	
	void splaying(int now, int gl)
	{
		if(!gl) rt = now;
		while(spl[now].fa != gl)
		{
			int f = spl[now].fa, g = spl[f].fa;
			if(g != gl) check(now, f) ^ check(f, g)? rotate(now) : rotate(f);
			rotate(now);
		}
	}
	int getnum(int rk)
	{
		int now = rt;
		while(1)
		{
			down(now); 
			if(rk <= spl[spl[now].son[0]].siz) now = spl[now].son[0];
			else if(rk <= spl[spl[now].son[0]].siz + 1) {splaying(now, 0); return spl[now].val;}
			else rk -= spl[spl[now].son[0]].siz + 1, now = spl[now].son[1];
		}
	}
	int getrank(int w)
	{
		int now = rt;
		while(now)
		{
			down(now);
			if(w <= spl[spl[now].son[0]].siz) now = spl[now].son[0];
			else if(w <= spl[spl[now].son[0]].siz + 1) return now;
			else w -= spl[spl[now].son[0]].siz + 1, now = spl[now].son[1];
		}	
	}
	void reverse(int l, int r)
	{
		if(l >= r) return;
		int x = getrank(l - 1), y = getrank(r + 1);
		splaying(x, 0); splaying(y, x);
		spl[spl[y].son[0]].flag ^= 1;
	}
}splay;

对于每个需要翻转的区间,应对 (l + 1, r + 1) 进行操作

权值建树
struct SPLAY_VALUE
{
	int cnt, rt;
	struct SF {int son[2], fa, siz, val, cnt; }spl[MAXN];
	void update(int now) {spl[now].siz = spl[spl[now].son[0]].siz + spl[spl[now].son[1]].siz + spl[now].cnt;}
	bool check(int now, int fa) {return spl[fa].son[1] == now;}
	void connect(int now, int fa, int fl) {spl[fa].son[fl] = now; spl[now].fa = fa;}
	void newnode(int &now, int fa, int val)
	{
		spl[now = ++cnt].val = val; spl[now].fa = fa;
		spl[now].cnt = spl[now].siz = 1;
	}
	void rotate(int now)
	{
		int f = spl[now].fa, g = spl[f].fa, fl = check(now, f);
		connect(spl[now].son[fl ^ 1], f, fl); 
		connect(now, g, check(f, g)); connect(f, now, fl ^ 1);
		update(f); update(now); 
	}
	void splaying(int now, int gl)
	{
		if(!gl) rt = now;
		while(spl[now].fa != gl)
		{
			int f = spl[now].fa, g = spl[f].fa;
			if(g != gl) check(now, f) ^ check(f, g)? rotate(now) : rotate(f); 
			rotate(now);
		}
	} 
	void delnode(int now)
	{
		splaying(now, 0);
		if(spl[now].cnt > 1) spl[now].cnt--;
		else if(spl[now].son[1])
		{
			int p = spl[now].son[1];
			while(spl[p].son[0]) p = spl[p].son[0];
			splaying(p, now);
			connect(spl[now].son[0], p, 0);
			rt = p; spl[p].fa = 0; update(rt); 
		}
		else rt = spl[now].son[0], spl[rt].fa = 0;
	}
	void delet(int w, int now)
	{
		if(w == spl[now].val) delnode(now);
		else if(w < spl[now].val) delet(w, spl[now].son[0]);
		else delet(w, spl[now].son[1]);
	}
	void insert(int w, int &now, int fa)
	{
		if(!now) newnode(now, fa, w), splaying(now, 0);
		else if(w < spl[now].val) insert(w, spl[now].son[0], now);
		else if(w > spl[now].val) insert(w, spl[now].son[1], now);
		else spl[now].cnt++, splaying(now, 0);
	}
	int getnum(int rk)
	{
		int now = rt;
		while(now)
		{
			int lsize = spl[spl[now].son[0]].siz;
			if(lsize + 1 <= rk && rk <= lsize + spl[now].cnt)
			{
				splaying(now, 0);
				return spl[now].val;
			}
			else if(lsize >= rk) now = spl[now].son[0];
			else rk -= lsize + spl[now].cnt, now = spl[now].son[1];
		}
	}
	int getrank(int w)
	{
		int now = rt, rk = 1;
		while(now)
		{
			if(spl[now].val == w) 
			{
				rk += spl[spl[now].son[0]].siz;
				splaying(now, 0); break;
			}
			if(w <= spl[now].val) now = spl[now].son[0];
			else rk += spl[spl[now].son[0]].siz + spl[now].cnt, now = spl[now].son[1];
		}
		return rk; 
	}
}splay;

查询前驱splay.getnum(splay.getrank(x) - 1)

查询后继splay.getnum(splay.getrank(x + 1))

并查集

int getfa(int x) {return fa[x] = fa[x] == x? x : getfa(fa[x]);}

图论

Harvel

给定所有点度数,判断能否构成简单图

flag = 1, n = read();
for(int i = 1; i <= n; i++) a[i] = read();
sort(a + 1, a + n + 1, cmp);
for(int i = 1; i <= n; i++)
{
	sort(a + i + 1, a + n + 1, cmp); 
    if(i + a[i] > n || a[i] < 0) {flag = 0; break;} 
	for(int j = 1; j <= a[i]; j++) a[i + j]--;
}	
puts(!flag? "no" : "yes");

网络流

最大权闭合子图/最大点权独立集/最小点权覆盖
struct DICNIC
{
	int st, gl, cnt, sum;
	int hed[MAXN], lel[MAXN], cur[MAXN];
	struct SF {int to, nxt; ll cap;}edg[MAXM << 1];
	void add_edge(int x, int y, ll c)
	{
		edg[++cnt].to = y; edg[cnt].cap = c; edg[cnt].nxt = hed[x]; hed[x] = cnt;
		edg[++cnt].to = x; edg[cnt].cap = 0; edg[cnt].nxt = hed[y]; hed[y] = cnt;
	}
	void init()
	{
		st = sum = 0; cnt = -1; gl = m + n + 1; 
		rep(i, 0, gl) hed[i] = -1;
	}
	bool bfs()
	{
		queue <int> Q; rep(i, st, gl) lel[i] = 0;
		int u, v; Q.push(st); lel[st] = 1;
		while(!Q.empty())
		{
			int u = Q.front(); Q.pop();
			for(int i = hed[u]; ~i; i = edg[i].nxt)
			{
				v = edg[i].to;
				if(!lel[v] && edg[i].cap > 0) {lel[v] = lel[u] + 1; Q.push(v);}
			}
		}
		return (lel[gl]);
	}
	ll dfs(int u, ll flow)
	{
		if(u == gl) return flow;
		int v; ll nflow = 0, tmp;
		for(int &i = cur[u]; ~i; i = edg[i].nxt)
		{
			v = edg[i].to;
			if(lel[v] == lel[u] + 1 && edg[i].cap > 0)
			{
				tmp = dfs(v, min(flow, edg[i].cap));
				flow -= tmp, edg[i].cap -= tmp;
				nflow += tmp, edg[i ^ 1].cap += tmp;
				if(!flow) break;
			}
		}
		if(!flow) lel[u] = 0;
		return nflow;
	}
	ll dicnic()
	{
		ll tmp, res = 0;
		while(bfs())
		{
			rep(i, st, gl) cur[i] = hed[i];
			while(tmp = dfs(st, INF)) res += tmp;
		}
		return res;
	}
}dic;
对偶图

ICPC-Beijing2006狼抓兔子 诸如此类不相交的图(常见于网格图)求最小割,当数据规模较大时可转成对偶图求解

struct SF
{
	int to, nxt, vlu;
}grf[3 * MAXN];
struct SFes
{
	int id, vlu;
	friend bool operator <(const SFes &a, const SFes &b)
	{
		return a.vlu > b.vlu;
	}
};
priority_queue <SFes> Q;
void add(int x, int y, int v)
{
	grf[++cnt].to = y; grf[cnt].nxt = hed[x]; grf[cnt].vlu = v; hed[x] = cnt;
	grf[++cnt].to = x; grf[cnt].nxt = hed[y]; grf[cnt].vlu = v; hed[y] = cnt;
}
void dij(int s)
{
	dis[s] = 0;
	SFes u, v;
	u.id = s, u.vlu = 0;
	Q.push(u);
	while(!Q.empty())
	{
		u = Q.top(); Q.pop();
		mark[u.id] = 1;
		for(int i = hed[u.id]; i; i = grf[i].nxt)
		{
			v.id = grf[i].to;
			if(!mark[v.id] && dis[v.id] > u.vlu + grf[i].vlu)
			{
				v.vlu = dis[v.id] = u.vlu + grf[i].vlu;
				Q.push(v);
			}
		}
	}
}
int main()
{
	n = re, m = re; 
	mst(dis, 0x7f);
	rep(i, 1, n)
	{ 
		rep(j, 1, m - 1)
		{
			int x, y, c = re;
			if(i == 1) x = 1, y = j * 2 + 1;
			else if(i == n) x = (n - 1) * (m - 1) * 2 + 2, y = ((i - 2) * (m - 1) + j) * 2;
			else x = ((i - 2) * (m - 1) + j) * 2, y = ((i - 1) * (m - 1) + j) * 2 + 1;
			add(x, y, c);
		}
	}
	rep(i, 1, n - 1)
	{
		rep(j, 1, m)
		{
			int x, y, c = re;
			if(j == 1) x = (n - 1) * (m - 1) * 2 + 2, y = ((i - 1) * (m - 1) + j) * 2;
			else if(j == m) x = 1, y = ((i - 1) * (m - 1) + j - 1) * 2 + 1;
			else x = ((i - 1) * (m - 1) + j - 1) * 2 + 1, y = ((i - 1) * (m - 1) + j) * 2;
			add(x, y, c);
		}
	}
	rep(i, 1, n - 1)
	{
		rep(j, 1, m - 1)
		{
			int x, y, c = re;
			x = ((i - 1) * (m - 1) + j) * 2, y = ((i - 1) * (m - 1) + j) * 2 + 1;
			add(x, y, c);
		}
	}
	n = (n - 1) * (m - 1) * 2 + 2;
	dij(1); 
	printf("%d\n", dis[n]);
	return 0;
}
MCMF
struct MCMF
{
	int st, gl, cnt; ll mflow, mcost;
	int pre[MAXN], hed[MAXN], mark[MAXN], cost[MAXN];
	struct SF{int u, v, nxt; ll cap, val;}edg[MAXM << 1];
	void init(int x) 
	{
		cnt = -1, st = 0, gl = x * 2 + 1;
		rep(i, 0, gl) hed[i] = -1;
	}	
	void add_edge(int x, int y, int c, int v)
	{ 
		edg[++cnt].u = x, edg[cnt].v = y, edg[cnt].cap = c, edg[cnt].val = v, edg[cnt].nxt = hed[x], hed[x] = cnt;
		edg[++cnt].u = y, edg[cnt].v = x, edg[cnt].cap = 0, edg[cnt].val = -v, edg[cnt].nxt = hed[y], hed[y] = cnt;
	}
	bool spfa()
	{
		queue <int> Q;
		rep(i, 0, gl) mark[i] = 0, pre[i] = -1, cost[i] = INF;
		mark[st] = 1; cost[st] = 0; Q.push(st); 
		while(!Q.empty())
		{
			int u = Q.front(); mark[u] = 0; Q.pop(); 
			for(int i = hed[u]; ~i; i = edg[i].nxt)
			{
				int v = edg[i].v; 
				if(edg[i].cap > 0 && cost[v] > cost[u] + edg[i].val)
				{
					pre[v] = i; cost[v] = cost[u] + edg[i].val;
					if(!mark[v]) mark[v] = 1, Q.push(v);
				}
			}		
		}
		return (~pre[gl]);
	}
	ll Mcmf()
	{
		ll flow = 0;
		while(spfa())
		{
			mflow = INF; 
			for(int i = pre[gl]; ~i; i = pre[edg[i].u]) mflow = min(mflow, edg[i].cap);
			flow += mflow; 
			for(int i = pre[gl]; ~i; i = pre[edg[i].u]) 
				edg[i].cap -= mflow, edg[i ^ 1].cap += mflow;
			mcost += mflow * cost[gl];
		}
		return mcost;
	}
}mcmf; 
按时间拆点 分层图

网络流可按时间拆点建图。

P2754 家园/星际转移问题

最短路

Dijsktra

默认是双向边,还有起点终点记得改。

struct DIJSKTRA
{
	int st, gl, cnt; int hed[MAXN], mark[MAXN]; ll dis[MAXN];
	struct SF{int to, nxt; ll val;}edg[MAXM << 1];
	struct SFes
	{
		int id; ll dist;
		bool operator <(const SFes &a) const
		{
			return a.dist < dist;
		}
	};
	void add_edge(int x, int y, ll c)
	{
		edg[++cnt].to = y; edg[cnt].val = c; edg[cnt].nxt = hed[x]; hed[x] = cnt;
		edg[++cnt].to = x; edg[cnt].val = c; edg[cnt].nxt = hed[y]; hed[y] = cnt;
	}
	void init()
	{
		st = cnt = 0, gl = n;
		rep(i, 1, n) dis[i] = INF, mark[i] = 0;
	}
	void dijkstra()
	{
		SFes u, v;
		u.id = st, u.dist = 0; dis[u.id] = 0; 
		priority_queue <SFes> Q; Q.push(u);
		while(!Q.empty())
		{
			u = Q.top(); Q.pop();
			if(mark[u.id]) continue; mark[u.id] = 1;
			for(int i = hed[u.id]; i; i = edg[i].nxt)
			{
				v.id = edg[i].to; 
				if(dis[v.id] > dis[u.id] + edg[i].val)
				{
					v.dist = dis[v.id] = dis[u.id] + edg[i].val;
					Q.push(v);
				}
			}
		}
	}
}dij; 
SPFA
struct SPFA
{
	queue <int> Q;
	int st, gl, cnt; int hed[MAXN], mark[MAXN]; ll dis[MAXN];
	struct SF{int to, nxt; ll val;}edg[MAXM << 1];
	void add_edge(int x, int y, ll c)
	{
		edg[++cnt].to = y; edg[cnt].val = c; edg[cnt].nxt = hed[x]; hed[x] = cnt;
		edg[++cnt].to = x; edg[cnt].val = c; edg[cnt].nxt = hed[y]; hed[y] = cnt;
	}
	void init()
	{
		cnt = 0; st = 0, gl = n + 1;
		rep(i, 1, n) hed[i] = mark[i] = 0, dis[i] = INF;
	}
	void spfa()
	{
		int u, v; 
		Q.push(st); mark[st] = 1, dis[st] = 0;
		while(!Q.empty())
		{
			u = Q.front(); mark[u] = 0; Q.pop();
			for(int i = hed[u]; i; i = edg[i].nxt)
			{
				v = edg[i].to;
				if(dis[v] > dis[u] + edg[i].val)
				{
					dis[v] = dis[u] + edg[i].val;
					if(!mark[v]) Q.push(v);
				}
			}
		}
	}
}spfa; 
Floyd
int floyd()
{
	for(int k = 1; k <= n; k++)
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= n; j++)
				if(dis[i][j] < dis[i][k] * dis[k][j])
					dis[i][j] = dis[i][k] * dis[k][j];
	for(int i = 1; i <= n; i++)
		if(dis[i][i] > 1) return 1;
	return 0;
}

Tarjan

点双
缩点
struct TARJAN
{
	static const int MAXM = 5e4 + 11, MAXN = 1e4 + 11; 
	int tp, cnt, num, ring;
	struct SF{int to, nxt;}edg[MAXM << 1];
	int hed[MAXN], sta[MAXN], low[MAXN], dfn[MAXN], anti[MAXN], insta[MAXN], belong[MAXN], rnum[MAXN];
	void add_edge(int x, int y)
	{
		edg[++cnt].to = y; edg[cnt].nxt = hed[x]; hed[x] = cnt;
		edg[++cnt].to = x; edg[cnt].nxt = hed[y]; hed[y] = cnt;
	}
	void init(int N = MAXN)
	{
		cnt = num = tp = ring = 0;
		rep(i, 0, N) hed[i] = belong[i] = low[i] = dfn[i] = insta[i] = rnum[i] = 0;
	}
	void dfs(int u)
	{
		dfn[u] = low[u] = ++num; sta[++tp] = u; insta[u] = 1;
		for(int i = hed[u]; i; i = edg[i].nxt)
		{
			int v = edg[i].to;
			if(!dfn[v]) dfs(v), low[u] = min(low[u], low[v]);
			else if(insta[v]) low[u] = min(low[u], dfn[v]);
		}
		if(dfn[u] == low[u])
		{
			int v = -1; ring++;
			while(u != v)
			{
				v = sta[tp--];
				++rnum[ring];
				belong[v] = ring;
			}
		}
	}
	void rebuild(int N)
	{
		rep(u, 1, N)
			for(int i = hed[u]; i; i = edg[i].nxt)
				if(belong[u] != belong[edg[i].to]) anti[belong[u]]++; 
	}
	void tarjan(int N) {rep(i, 1, N) if(!dfn[i]) dfs(i);}
}tj;
割点

2-SAT

最大团

矩阵树定理

无向图的生成树个数

例题:bzoj4031小z的房间

有时部分情况需要特判一下,不能纯贴板子quq

int mov[5][5] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int det(int x)
{
	rep(i, 1, x) rep(j, 1, x) a[i][j] = (a[i][j] + MOD) % MOD;
	ll res = 1, f = 1;
	rep(i, 1, x)
	{
		rep(j, i + 1, x)
		{
			ll A = a[i][i], B = a[j][i];
			while(B)
			{
				ll tmp = A / B; A %= B; swap(A, B);
				rep(k, i, x) a[i][k] = (a[i][k] - tmp * a[j][k] % MOD + MOD) % MOD;
				rep(k, i, x) swap(a[i][k], a[j][k]); 
				f = -f;
			} 
		}
		if(!a[i][i]) return 0;
		res = (res * a[i][i]) % MOD;
	}
	if(f == -1) return (MOD - res) % MOD;
	return res;
}
int main()
{
	n = re, m = re;
	rep(i, 1, n) scanf("%s", str[i] + 1);
	rep(i, 1, n) rep(j, 1, m) if(str[i][j] == '.') p[i][j] = ++cnt;
	rep(i, 1, n)
	{
		rep(j, 1, m)
		{
			if(str[i][j] == '.')
			{
				rep(k, 0, 3)
				{
					int nx = i + mov[k][0], ny = j + mov[k][1];
					if(nx < 1 || ny < 1 || nx > n || ny > m || str[nx][ny] != '.') continue;
					int u = p[i][j], v = p[nx][ny];
					a[u][v]--, a[u][u]++;
				}
			}
		}
	} 
	printf("%d\n", det(cnt - 1)); //!!!不要忘记cnt-1
	return 0;
}
  • 2.给出有向图和其中的一个点,求以这个点为根的生成外向树个数。

  • 3.给出有向图和其中一个点,求以这个点为根的生成内向树个数。

计算几何

凸包

struct SF
{
	double x, y;
	SF(double x = 0, double y = 0) : x(x), y(y){};
}grf[MAXN], sta[MAXN];
int sgn(double a) return (fabs(a) < eps)? 0 : (a > 0)? 1 : -1;
bool operator <(const SF& a, const SF& b)
{
	return (!sgn(a.x - b.x))? a.y < b.y : a.x < b.x; 
} 
SF operator -(SF a, SF b)
{
	return SF(a.x - b.x, a.y - b.y);
}
double cross(SF a, SF b)
{
	return (a.x * b.y - b.x * a.y);
}
double turn(SF a, SF b, SF nw)
{
	SF A = b - a;
	SF B = nw - a;
	return cross(A, B);
}
double length(SF a, SF b)
{
	return sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));
}
void convexhull()
{
	ans = 0;
	sort(grf + 1, grf + n + 1);
	sta[0] = grf[1], sta[1] = grf[2];
	int top = 1;
	for(int i = 3; i <= n; i++)
	{
		while(top && turn(sta[top - 1], sta[top], grf[i]) <= 0) top--;
		sta[++top] = grf[i];
	}
	for(int i = n - 1, tmp = top; i >= 1; i--)
	{
		while(top > tmp && turn(sta[top - 1], sta[top], grf[i]) <= 0) top--;
		sta[++top] = grf[i];
	}
	for(int i = 1; i <= top; i++) ans += length(sta[i - 1], sta[i]);
} 

旋转卡壳

N个点求最大四边形面积 O(N^2)
struct SF
{
	ll x, y;
	SF(ll x = 0, ll y = 0) : x(x), y(y){};
}p[MAXN], s[MAXN];
int sgn(ll a){return ((abs(a) < eps)? 0 : ((a > 0)? 1 : -1));} 
bool operator <(const SF &a, const SF &b)
{
	return (!sgn(a.x - b.x))? a.y < b.y : a.x < b.x; 
} 
SF operator -(SF a, SF b)
{
	return SF(a.x - b.x, a.y - b.y);
}
ll operator *(SF a, SF b)
{
	return (a.x * b.y - b.x * a.y);
}
ll turn(SF a, SF b, SF nw)
{
	SF A = b - a, B = nw - a;
	return A * B;
}
ll convexhull()
{
	ll ans = 0;
	sort(p + 1, p + n + 1);
	int top = 1; s[0] = p[1], s[1] = p[2];
	for(int i = 3; i <= n; i++)
	{
		while(top && turn(s[top - 1], s[top], p[i]) < 0) top--; //此处共线的点也应算入
		s[++top] = p[i];
	}
	for(int i = n - 1, tmp = top; i >= 1; i--)
	{
		while(top > tmp && turn(s[top - 1], s[top], p[i]) < 0) top--;
		s[++top] = p[i];
	} 
	s[top + 1] = p[1];
	if(top <= 2) return 0ll;
	else if(top == 3) //不保证为凸四边形,三个点的情况特殊讨论
	{
		ll res = INF; ans = abs((s[2] - s[1]) * (s[3] - s[1])); 
		rep(i, 1, n)
		{
			ll t1 = abs((s[2] - s[1]) * (p[i] - s[1])), 
			   t2 = abs((s[2] - s[3]) * (p[i] - s[3])), 
			   t3 = abs((s[3] - s[1]) * (p[i] - s[1]));
			if(t1) res = min(res, t1); 
			if(t2) res = min(res, t2); 
			if(t3) res = min(res, t3);
		}
		return ans - res; 
	} 
	rep(i, 1, top)
	{
		int a = i % top + 1, b = (i + 2) % top + 1;
		rep(j, i + 2, top)
		{
			while((a % top + 1) != j && abs((s[j] - s[i]) * (s[a + 1] - s[i])) > abs((s[j] - s[i]) * (s[a] - s[i]))) a = a % top + 1;
			while((b % top + 1) != i && abs((s[j] - s[i]) * (s[b + 1] - s[i])) > abs((s[j] - s[i]) * (s[b] - s[i]))) b = b % top + 1;
			ans = max(ans, abs((s[j] - s[i]) * (s[a] - s[i])) + abs((s[b] - s[i]) * (s[j] - s[i])));
		}
	}
	return ans;
} 

扫描线

求二维平面线段划分平面数

CF1401E. Divide Square

struct SF
{
	int x, y, op;	
	bool operator <(const SF& a)const {return x < a.x;}
}seg[MAXN];
struct SFes
{
	int l, r, x;
	bool operator <(const SFes& a)const {return x < a.x;}
}lin[MAXN];
int lowbit(int x) {return x & (-x);}
void update(int x, int t) {for(int i = x; i <= INF; i += lowbit(i)) c[i] += t;}
ll query(int x) {ll res = 0; for(int i = x; i; i -= lowbit(i)) res += c[i]; return res;}
int main() 
{
	n = re, m = re; ans = 1ll; 
	rep(i, 1, n) 
	{
		int y = re, l = re, r = re; ans += (l == 0 && r == INF);
		seg[++cnt] = {l, y, 1}; seg[++cnt] = {r + 1, y, -1};
	} 
	rep(i, 1, m) lin[i].x = re, lin[i].l = re, lin[i].r = re, 
				 ans += (lin[i].l == 0 && lin[i].r == INF);
	sort(seg + 1, seg + cnt + 1); sort(lin + 1, lin + m + 1);
	for(int i = 1, j = 0; i <= m; i++)
	{
		while(j < cnt && seg[j + 1].x <= lin[i].x) j++, update(seg[j].y + 1, seg[j].op);
		ans += query(lin[i].r + 1) - query(lin[i].l);
	}
	printf("%lld\n", ans);
	return 0;
}
N个矩形的面积并

洛谷P5490

const int MAXN = 1e6 + 11;
int n, m; ll ans; ll X[MAXN << 1];
struct SF
{
	ll l, r, h; int fl;
	bool operator <(const SF &a) const{return h < a.h;}
}seg[MAXN << 1];
struct SFes
{
	ll len; int l, r, sum;
}tree[MAXN << 2];
void pushup(int now)
{
	int l = tree[now].l, r = tree[now].r;
	if(tree[now].sum) tree[now].len = X[r + 1] - X[l];
	else tree[now].len = tree[now << 1].len + tree[now << 1 | 1].len;
}
void build(int now, int l, int r)
{
	tree[now].l = l, tree[now].r = r;
	tree[now].len = tree[now].sum = 0;
	if(l == r) return;
	int mid = (l + r) >> 1;
	build(now << 1, l, mid); build(now << 1 | 1, mid + 1, r);
}
void insert(int now, ll L, ll R, int flag)
{
	int l = tree[now].l, r = tree[now].r;
	if(X[r + 1] <= L || X[l] >= R) return;
	if(X[l] >= L && X[r + 1] <= R)
	{
		tree[now].sum += flag;
		pushup(now); return;
	}
	insert(now << 1, L, R, flag); insert(now << 1 | 1, L, R, flag); 
	pushup(now);
}
int solve()
{
	rep(i, 1, n)
	{
		ll X1 = re, Y1 = re, X2 = re, Y2 = re;
		X[2 * i - 1] = X1; seg[2 * i - 1] = {X1, X2, Y1, 1};
		X[2 * i] = X2; seg[2 * i] = {X1, X2, Y2, -1};
	}
	n <<= 1; sort(seg + 1, seg + n + 1); sort(X + 1, X + n + 1);
	m = unique(X + 1, X + n + 1) - X - 1;
	build(1, 1, m - 1);
	rep(i, 1, n - 1)
	{
		insert(1, seg[i].l, seg[i].r, seg[i].fl);
		ans += tree[1].len * (seg[i + 1].h - seg[i].h);
	}
	return printf("%lld\n", ans);
}

数论

组合数预处理

void init_C(int n) {
    inv[1] = 1;
    for(int i = 2; i <= n; i++)
        inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD;
    fac[0] = 1, invfac[0] = 1;
    for(int i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i % MOD;
        invfac[i] = invfac[i - 1] * inv[i] % MOD;
    }
}
ll C(ll n, ll m) {
    if(n < 0 || m < 0 || n - m < 0)
        return 0;
    return fac[n] * invfac[n - m] % MOD * invfac[m] % MOD;
}

高斯消元

void Gauss()
{
	int mx; 
	rep(i, 1, n)
	{
		mx = i;
		rep(j, i + 1, n) if(fabs(a[j][i]) > fabs(a[mx][i])) mx = j;
		if(mx != i) swap(a[mx], a[i]);
		if(!a[i][i]) {flag = 0; return;}
		rep(j, 1, n)
			if(i != j)
			{rep(k, i + 1, n + 1) a[j][k] -= a[i][k] * (a[j][i] / a[i][i]); a[j][i] = 0;}
	}
}

gcd & exgcd

int gcd(int a, int b){return !b? a : gcd(b, a % b);}
int exgcd(int k, int t, int &x, int &y)
{
	if(!t) 
	{
		x = 1, y = 0;
		return k;
	}
	int tmp, g = exgcd(t, k % t, x, y);
	tmp = y; y = x - k / t * y; x = tmp;
	return g;
}

求行列式

int det(int x)
{
	rep(i, 1, x) rep(j, 1, x) a[i][j] = (a[i][j] + MOD) % MOD;
	ll res = 1, f = 1;
	rep(i, 1, x)
	{
		rep(j, i + 1, x)
		{
			ll A = a[i][i], B = a[j][i];
			while(B)
			{
				ll tmp = A / B; A %= B; swap(A, B);
				rep(k, i, x) a[i][k] = (a[i][k] - tmp * a[j][k] % MOD + MOD) % MOD;
				rep(k, i, x) swap(a[i][k], a[j][k]); 
				f = -f;
			} 
		}
		if(!a[i][i]) return 0;
		res = (res * a[i][i]) % MOD;
	}
	if(f == -1) return (MOD - res) % MOD;
	return res;
}

组合数性质

  • 组合数取模

C n m m o d    p = C n p m p × C n m o d    p m m o d    p m o d    p C_n^m \mod p=C_{\frac{n}p}^{\frac{m}p}\times C_{n\mod p}^{m\mod p}\mod p Cnmmodp=Cpnpm×Cnmodpmmodpmodp

  • 按行求和

[公式]

  • 杨辉三角性质
    C n k + C n k − 1 = n ! k ! ( n − k ) ! + n ! ( k − 1 ) ! ( n − k + 1 ) ! = n ! ( n − k + 1 ) + k k ! ( n − k + 1 ) ! = C n + 1 k C_n^k+C_n^{k-1}=\frac{n!}{k!(n-k)!}+\frac{n!}{(k-1)!(n-k+1)!}=n!\frac{(n-k+1)+k}{k!(n-k+1)!}=C_{n+1}^k Cnk+Cnk1=k!(nk)!n!+(k1)!(nk+1)!n!=n!k!(nk+1)!(nk+1)+k=Cn+1k

  • 可将变化量 [公式] 从两个组合数的范围缩小至一个组合数中
    C n r C r k = C n k C n − k r − k C_n^rC_r^k=C_n^kC_{n-k}^{r-k} CnrCrk=CnkCnkrk

  • 就是 [公式] 个相同的球中不重复地选出 [公式] 个球的组合数
    C m + n k = ∑ i = 0 k C m i C n k − i C_{m+n}^k=\displaystyle\sum_{i=0}^kC_m^iC_n^{k-i} Cm+nk=i=0kCmiCnki

  • 化去组合数外的变化量

k C n k = k n ! k ! ( n − k ) ! = n ( n − 1 ) ! ( k − 1 ) ! ( n − k ) ! = n C n − 1 k − 1 kC_n^k=k\frac{n!}{k!(n-k)!}=n\frac{(n-1)!}{(k-1)!(n-k)!}=nC_{n-1}^{k-1} kCnk=kk!(nk)!n!=n(k1)!(nk)!(n1)!=nCn1k1

  • 组合数和2幂次的关系1
    1 C n 1 + 2 C n 2 + 3 C n 3 + . . . + n C n n = n 2 n − 1 1C_n^1+2C_n^2+3C_n^3+...+nC_n^n=n2^{n-1} 1Cn1+2Cn2+3Cn3+...+nCnn=n2n1

  • 组合数和2幂次的关系2
    1 2 C n 1 + 2 2 C n 2 + 3 2 C n 3 + . . . + n 2 C n n = n ( n + 1 ) 2 n − 2 1^2C_n^1+2^2C_n^2+3^2C_n^3+...+n^2C_n^n=n(n+1)2^{n-2} 12Cn1+22Cn2+32Cn3+...+n2Cnn=n(n+1)2n2

  • 杨辉三角整行/整列求和性质
    C 1 1 + C 2 1 + . . . + C n 1 = C n + 1 2 = n ∗ ( n + 1 ) 2 C_1^1+C_2^1+...+C_n^1=C_{n+1}^2=\frac{n*(n+1)}{2} C11+C21+...+Cn1=Cn+12=2n(n+1)

    C 1 2 + C 2 2 + C 3 2 + . . . + C n 2 = C n + 1 3 = n ( n + 1 ) ( n − 1 ) 6 C_1^2+C_2^2+C_3^2+...+C_n^2=C_{n+1}^3=\frac{n(n+1)(n-1)}{6} C12+C22+C32+...+Cn2=Cn+13=6n(n+1)(n1)

  • 组合数平方求和

∑ i = 0 n ( C n i ) 2 = C 2 n n \displaystyle\sum_{i=0}^n(C_n^i)^2=C_{2n}^n i=0n(Cni)2=C2nn

  • 和差化积(?
    ∑ i = k + 1 n C i − 1 i − k − 1 = C n n − k − 1 \displaystyle\sum_{i=k+1}^nC_{i-1}^{i-k-1}=C_n^{n-k-1} i=k+1nCi1ik1=Cnnk1

Catalan数

C n = ∑ i = 1 n C i − 1 C n − i , C 0 = 1 C_n=\displaystyle\sum_{i=1}^nC_{i-1}C{n-i},C_0=1 Cn=i=1nCi1Cni,C0=1,则我们称 C n C_n Cn 为第n个Catalan数。

我们知道:其普通生成函数为 1 − 1 − 4 x 2 x \frac{1-\sqrt{1-4x}}{2x} 2x114x ,求得通项公式为 C n = C 2 n n n + 1 C_n= \frac{C_{2n}^n}{n+1} Cn=n+1C2nn

例:1,2,···,n 这些数字依次压入栈中(可以随时进行pop操作),那么最后将栈中元素弹出直至栈为空可以得到一个出栈序列。所有这样的不同出栈序列数即为 C n C_n Cn

Fabonacci数列

常用数学公式

三角函数
  • 和差角公式
    c o s ( A ± B ) = c o s A c o s B ∓ s i n A s i n B s i n ( A ± B ) = s i n A c o s B ± c o s A s i n B t a n ( A ± B ) = t a n A ± t a n B 1 ∓ t a n A t a n B cos(A\pm B)=cosAcosB\mp sinAsinB \\ sin(A\pm B)=sinAcosB\pm cosAsinB \\ tan(A\pm B)=\frac{tanA\pm tanB}{1\mp tanAtanB} cos(A±B)=cosAcosBsinAsinBsin(A±B)=sinAcosB±cosAsinBtan(A±B)=1tanAtanBtanA±tanB

  • 和差化积公式
    s i n A ± s i n B = 2 s i n A ± B 2 c o s A ∓ B 2 c o s A + c o s B = 2 c o s A + B 2 c o s A − B 2 c o s A − c o s B = − 2 s i n A + B 2 s i n A − B 2 t a n A ± t a n B = s i n ( A ± B ) c o s A c o s B c o t A ± c o t B = ± s i n ( A ± B ) s i n A s i n B sinA\pm sinB=2sin\frac{A\pm B}{2}cos\frac{A\mp B}{2} \\ cosA+cosB=2cos\frac{A+B}{2}cos\frac{A-B}{2} \\ cosA-cosB=-2sin\frac{A+B}{2}sin\frac{A-B}{2} \\ tanA\pm tanB=\frac{sin(A\pm B)}{cosAcosB} \\ cotA\pm cotB=\pm \frac{sin(A\pm B)}{sinAsinB} sinA±sinB=2sin2A±Bcos2ABcosA+cosB=2cos2A+Bcos2ABcosAcosB=2sin2A+Bsin2ABtanA±tanB=cosAcosBsin(A±B)cotA±cotB=±sinAsinBsin(A±B)

  • 积化和差公式
    s i n A c o s B = 1 2 [ s i n ( A + B ) + s i n ( A − B ) ] c o s A s i n B = 1 2 [ s i n ( A + B ) − s i n ( A − B ) ] c o s A c o s B = 1 2 [ c o s ( A + B ) + c o s ( A − B ) ] s i n A s i n B = 1 2 [ c o s ( A + B ) − c o s ( A − B ) ] sinAcosB=\frac{1}{2}[sin(A+B)+sin(A-B)] \\ cosAsinB=\frac{1}{2}[sin(A+B)-sin(A-B)] \\ cosAcosB=\frac{1}{2}[cos(A+B)+cos(A-B)] \\ sinAsinB=\frac{1}{2}[cos(A+B)-cos(A-B)] sinAcosB=21[sin(A+B)+sin(AB)]cosAsinB=21[sin(A+B)sin(AB)]cosAcosB=21[cos(A+B)+cos(AB)]sinAsinB=21[cos(A+B)cos(AB)]

  • 二倍角公式
    s i n 2 A = 2 s i n A c o s A c o s 2 A = 2 c o s 2 A − 1 = 1 − 2 s i n 2 A = c o s 2 A − s i n 2 A t a n 2 A = 2 t a n A 1 − t a n 2 A sin2A=2sinAcosA \\ cos2A=2cos^2A-1=1-2sin^2A=cos^2A-sin^2A \\ tan2A=\frac {2tanA}{1-tan^2A} sin2A=2sinAcosAcos2A=2cos2A1=12sin2A=cos2Asin2Atan2A=1tan2A2tanA

数列
  • 等差数列求和

S n = n a 1 + n ( n − 1 ) d 2 S_n=na_1+\frac{n(n-1)d}{2} Sn=na1+2n(n1)d

  • 等比数列求和
    S n = a 1 ( 1 − q n ) 1 − q S_n=\frac{a_1(1-q^n)}{1-q} Sn=1qa1(1qn)

STL函数

Bitset

foo.size() 返回大小(位数)
foo.count() 返回1的个数
foo.any() 返回是否有1
foo.none() 返回是否没有1
foo.set() 全都变成1
foo.set(p) 将第p + 1位变成1
foo.set(p, x) 将第p + 1位变成x
foo.reset() 全都变成0
foo.reset(p) 将第p + 1位变成0
foo.flip() 全都取反
foo.flip(p) 将第p + 1位取反
foo.to_ulong() 返回它转换为unsigned long的结果,如果超出范围则报错
foo.to_ullong() 返回它转换为unsigned long long的结果,如果超出范围则报错
foo.to_string() 返回它转换为string的结果

String

str.size();//返回字符串长度
str.length();//返回字符串长度
str.empty();//检查 str 是否为空,为空返回 1,否则返回 0
str1.swap(str2);//把 str1 与 str2 交换
str.erase(3);//删除 [3] 及以后的字符,并返回新字符串
str.erase(3, 5);//删除从 [3] 开始的 5 个字符,并返回新字符串
str.push_back('a');//在 str 末尾添加字符'a'
str.append("abc");//在 str 末尾添加字符串"abc"
str.insert(2, "sz");//从 [2] 位置开始添加字符串 "sz",并返回形成的新字符串
str.insert(2, "abcd", 3);//从 [2] 位置开始添加字符串 "abcd" 的前 3 个字符,并返回形成的新字符串
str.insert(2, "abcd", 1, 3);//从 [2] 位置开始添加字符串 "abcd" 的前 [2]~[2+(3-1)] 个字符,并返回形成的新字符串
str.replace(2, 4, "sz");//返回把 [2]~[2+(4-1)] 的内容替换为 "sz" 后的新字符串
str.replace(2, 4, "abcd", 3);//返回把 [2]~[2+(4-1)] 的内容替换为 "abcd" 的前3个字符后的新字符串
str.substr(3); //返回 [3] 及以后的子串
str.substr(2, 4); //返回 str[2]~str[2+(4-1)] 子串(即从[2]开始4个字符组成的字符串)
str.find("ab");//返回字符串 ab 在 str 的位置
str.find("ab", 2);//在 str[2]~str[n-1] 范围内查找并返回字符串 ab 在 str 的位置
str.rfind("ab", 2);//在 str[0]~str[2] 范围内查找并返回字符串 ab 在 str 的位置

//first 系列函数
str.find_first_of("apple");//返回 apple 中任何一个字符首次在 str 中出现的位置
str.find_first_of("apple", 2);//返回 apple 中任何一个字符首次在 str[2]~str[n-1] 范围中出现的位置
str.find_first_not_of("apple");//返回除 apple 以外的任何一个字符在 str 中首次出现的位置
str.find_first_not_of("apple", 2);//返回除 apple 以外的任何一个字符在 str[2]~str[n-1] 范围中首次出现的位置

//last 系列函数
str.find_last_of("apple");//返回 apple 中任何一个字符最后一次在 str 中出现的位置
str.find_last_of("apple", 2);//返回 apple 中任何一个字符最后一次在 str[0]~str[2] 范围中出现的位置
str.find_last_not_of("apple");//返回除 apple 以外的任何一个字符在 str 中最后一次出现的位置
str.find_last_not_of("apple", 2);//返回除 apple 以外的任何一个字符在 str[0]~str[2] 范围中最后一次出现的位置

//以上函数如果没有找到,均返回string::npos
cout << string::npos;
string s(str):生成字符串为str的复制品 
string s(str, strbegin,strlen):将字符串str中从下标strbegin开始、长度为strlen的部分作为字符串初值 
string s(cstr, char_len):以C_string类型cstr的前char_len个字符串作为字符串s的初值 
string s(num ,c):生成num个c字符的字符串 
string s(str, stridx):将字符串str中从下标stridx开始到字符串结束的位置作为字符串初值

Set

set用法:

begin();            // 返回指向第一个元素的迭代器
end();              // 返回指向迭代器的最末尾处(即最后一个元素的下一个位置)
clear();            // 清除所有元素
count();            // 返回某个值元素的个数
empty();            // 如果集合为空,返回true
equal_range();      //返回集合中与给定值相等的上下限的两个迭代器
erase()–删除集合中的元素
find()–返回一个指向被查找到元素的迭代器
get_allocator()–返回集合的分配器
insert()–在集合中插入元素
lower_bound()–返回指向大于(或等于)某值的第一个元素的迭代器
key_comp()–返回一个用于元素间值比较的函数
max_size()–返回集合能容纳的元素的最大限值
rbegin()–返回指向集合中最后一个元素的反向迭代器
rend()–返回指向集合中第一个元素的反向迭代器
size()–集合中元素的数目
swap()–交换两个集合变量
upper_bound()–返回大于某个值元素的迭代器
value_comp()–返回一个用于比较元素间的值的函数

其他

宏定义

#include <bits/stdc++.h>
#define re read()
#define ll long long
#define pb(a) push_back(a)
#define mkp(a, b) make_pair(a, b)
#define mst(a, c) memset(a, c, sizeof(a))
#define rep(a, b, c) for(int (a) = (b); (a) <= (c); (a)++)
#define per(a, b, c) for(int (a) = (b); (a) >= (c); (a)--)
using namespace std;
int read()
{
	int num = 0; bool f = 0; char ch = getchar();
	while(ch < '0' || ch > '9') {f = (ch == '-'); ch = getchar();}
	while(ch >= '0' && ch <= '9') {num = (num << 1) + (num << 3) + ch - '0'; ch = getchar();}
	return f? -num : num;
}
//void add_edge(int x, int y, int c)
//{
//	edg[++cnt].to = y; edg[cnt].val = c; edg[cnt].nxt = hed[x]; hed[x] = cnt;
//	edg[++cnt].to = x; edg[cnt].val = c; edg[cnt].nxt = hed[y]; hed[y] = cnt;
//}
int work()
{

}
int main()
{
//	clock_t TAT = clock();
//=====================================
	per(_, re, 1) {work();}
//=====================================
//	cout<<"Time::"<<clock() - TAT<<"ms"<<'\n';
	return 0;
}


高精度

struct INTEGER
{
	vector <int> vec;
	static const int B = 1e4;
	INTEGER(ll x = 0)
	{
		while(x) vec.pb(x % B), x /= B;
		if(!vec.size()) vec.pb(0);
	}
	void init(int len = 1) {vec.clear(); vec.resize(len, 0);}
	void top0() {while(vec.size() > 1 && !vec.back()) vec.pop_back();}
	INTEGER operator + (const INTEGER &a) const
	{
		int len = max(vec.size(), a.vec.size());
		INTEGER tmp; tmp.init(len + 1); 
		rep(i, 0, len - 1)
		{
			if(i < vec.size()) tmp.vec[i] += vec[i];
			if(i < a.vec.size()) tmp.vec[i] += a.vec[i];
			if(tmp.vec[i] >= B) tmp.vec[i + 1]++, tmp.vec[i] -= B;
		}
		tmp.top0(); return tmp;
	}
	INTEGER operator * (const INTEGER &a) const
	{
		INTEGER tmp; tmp.init(a.vec.size() + vec.size() + 5);
		int n = vec.size(), m = a.vec.size();
		rep(i, 0, n - 1)
			rep(j, 0, m - 1)
			{
				tmp.vec[i + j] += vec[i] * a.vec[j];
				if(tmp.vec[i + j] >= 2e9) tmp.vec[i + j + 1] += tmp.vec[i + j] / B, tmp.vec[i + j] %= B;
			}	
		n = tmp.vec.size() - 1;
		rep(i, 0, n) if(tmp.vec[i] >= B) tmp.vec[i + 1] += tmp.vec[i] / B, tmp.vec[i] %= B;
		tmp.top0(); return tmp;
	}
	void read()
	{
		string str; cin>>str;  
		int t = 0, len = str.size(), las = len, f = (len % 4 != 0); 
		init(len / 4 + f); 
		for(int i = max(0, len - 4); i >= 0; i -= (i >= 4)? 4 : (!i)? 1 : i) 
		{
			int tmp = 0;
			for(int j = i; j < i + 4 && j < las; j++) tmp = tmp * 10 + str[j] - '0';
			vec[t++] = tmp; las = i;
		}
		top0();
	}
	void print() 
	{
		printf("%d", vec[vec.size() - 1]);
		per(i, vec.size() - 2, 0) printf("%04d", vec[i]);
	}
};
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值