数据结构
树状数组
基础
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
下标建树
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;
按时间拆点 分层图
网络流可按时间拆点建图。
最短路
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
最大团
矩阵树定理
无向图的生成树个数
有时部分情况需要特判一下,不能纯贴板子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;
}
扫描线
求二维平面线段划分平面数
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个矩形的面积并
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+Cnk−1=k!(n−k)!n!+(k−1)!(n−k+1)!n!=n!k!(n−k+1)!(n−k+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=CnkCn−kr−k -
就是 个相同的球中不重复地选出 个球的组合数
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=0∑kCmiCnk−i -
化去组合数外的变化量
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!(n−k)!n!=n(k−1)!(n−k)!(n−1)!=nCn−1k−1
-
组合数和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=n2n−1 -
组合数和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)2n−2 -
杨辉三角整行/整列求和性质
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)(n−1)
-
组合数平方求和
∑ i = 0 n ( C n i ) 2 = C 2 n n \displaystyle\sum_{i=0}^n(C_n^i)^2=C_{2n}^n i=0∑n(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+1∑nCi−1i−k−1=Cnn−k−1
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=1∑nCi−1Cn−i,C0=1,则我们称 C n C_n Cn 为第n个Catalan数。
我们知道:其普通生成函数为 1 − 1 − 4 x 2 x \frac{1-\sqrt{1-4x}}{2x} 2x1−1−4x ,求得通项公式为 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)=cosAcosB∓sinAsinBsin(A±B)=sinAcosB±cosAsinBtan(A±B)=1∓tanAtanBtanA±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±Bcos2A∓BcosA+cosB=2cos2A+Bcos2A−BcosA−cosB=−2sin2A+Bsin2A−BtanA±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(A−B)]cosAsinB=21[sin(A+B)−sin(A−B)]cosAcosB=21[cos(A+B)+cos(A−B)]sinAsinB=21[cos(A+B)−cos(A−B)] -
二倍角公式
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=2cos2A−1=1−2sin2A=cos2A−sin2Atan2A=1−tan2A2tanA
数列
- 等差数列求和
S n = n a 1 + n ( n − 1 ) d 2 S_n=na_1+\frac{n(n-1)d}{2} Sn=na1+2n(n−1)d
- 等比数列求和
S n = a 1 ( 1 − q n ) 1 − q S_n=\frac{a_1(1-q^n)}{1-q} Sn=1−qa1(1−qn)
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]);
}
};