板子5
LCA (倍增, 本地是求三个点的LCA)
struct Edge { int v, next; } e[maxn<<1]; int cnt, deep[maxn], head[maxn], n, up[maxn][20]; void init() { cnt = 0; memset(head, -1, sizeof(head)); } void add(int u, int v) { e[cnt].v = v; e[cnt].next = head[u]; head[u] = cnt++; } void dfs(int u, int fa, int dep) { deep[u] = dep; for (int i = 1; i < 20; i++) { up[u][i] = up[up[u][i-1]][i-1]; } for (int i = head[u]; ~i; i = e[i].next) { int v = e[i].v; if (v == fa) continue; up[v][0] = u; dfs(v, u, dep + 1); } } int LCA(int u, int v) { if (deep[u] < deep[v]) swap(u, v); int k = deep[u] - deep[v]; for (int i = 0; i < 20; i++) { if ((1<<i) & k) u = up[u][i]; } if (u != v) { for (int i = 19; i >= 0; i--) { if (up[u][i] != up[v][i]) { u = up[u][i]; v = up[v][i]; } } u = up[u][0]; } //cout << u << endl; return u; } //我觉得瓜瓜也是喜欢一个人的人 //可是以后会有狗狗一直陪着你的 //爱你哦 void solve() { int q; cin >> n >> q; init(); for (int i = 2; i <= n; i++) { int u; scanf("%d", &u); add(u, i); add(i, u); } up[1][0] = 1; dfs(1, -1, 0); for (int i = 1; i <= q; i++) { int a[10]; for (int i = 1; i <= 3; i++) scanf("%d", a + i); a[4] = a[1]; int maxx = -1, id; for (int i = 1; i <= 3; i++) { int x = LCA(a[i], a[i+1]); if (deep[x] > maxx) { maxx = deep[x]; id = x; } } int ans = 0; for (int i = 1; i <= 3; i++) { int x = LCA(id, a[i]); int dd = deep[id] + deep[a[i]] - 2 * deep[x]; ans = max(ans, dd); } printf("%d\n", ans + 1); } }
dij(领接表)
int cnt, head[maxn]; ll dis[maxn]; struct Edge { int v, next; ll w; //记住这里改顺序下面push进队一定要改! bool operator < (const Edge &rhs) const { return w > rhs.w; } } e[maxn*4]; void add(int u, int v, ll co) { e[cnt].v = v; e[cnt].w = co; e[cnt].next = head[u]; head[u] = cnt++; } void init() { cnt = 0; memset(head, -1, sizeof(head)); } void dij(int s, int len) { priority_queue<Edge> pq; for (int i = 1; i <= len; i++) dis[i] = inf; bool vis[maxn] = {0}; dis[s] = 0; pq.push((Edge){s, 0, 0}); while (!pq.empty()) { Edge tmp = pq.top(); pq.pop(); if (vis[tmp.v]) continue; vis[tmp.v] = 1; for (int i = head[tmp.v]; ~i; i = e[i].next) { Edge u = e[i]; if (dis[u.v] > dis[tmp.v] + u.w) { dis[u.v] = dis[tmp.v] + u.w; pq.push((Edge){u.v, 0, dis[u.v]}); } } } }
主席树(动态开点版本, 该题是求区间不同数的个数)
int cnt, T[maxn]; struct Tree{ int l, r, val; } tre[maxn*40]; void init() { cnt = 0; } void update(int &id, int pre, int l, int r, int pos, int val) { id = ++cnt; tre[id] = tre[pre]; tre[id].val += val; if (l == r) return; int mid = l + r >> 1; if (pos <= mid) update(tre[id].l, tre[pre].l, l, mid, pos, val); else update(tre[id].r, tre[pre].r, mid + 1, r, pos, val); } int query(int pos, int id, int l, int r) { if (l == r) return tre[id].val; int m = l + r >> 1; if (pos > m) { return tre[tre[id].l].val + query(pos, tre[id].r, m + 1, r); } else return query(pos, tre[id].l, l, m); }
二分图相关
一、二分图最大匹配定义:匹配是图中一些边的集合,且集合中任意两条边都没有公共点,所有的匹配中,边数最多的就是最大匹配。算法:用匈牙利算法可以在O(V*E)的复杂度内求出二分图的最大匹配,具体可以看byvoid神犇的blog,讲的很详细,不过想真正完全证明这个算法,得去看组合数学。二、二分图最小点覆盖定义:点覆盖是图中一些点的集合,且对于图中所有的边,至少有一个端点属于点覆盖,点数最小的覆盖就是最小点覆盖。定理:最小点覆盖=最大匹配。注:此处证明直接参考二分图中的最大匹配数等于最小点覆盖数的证明简单证明:首先必然有,最小覆盖>=最大匹配。于是只要证明不等式可以取等号,我们可在最大匹配的基础上构造出一组点覆盖。对右边每一个未匹配的点进行dfs找增广路,标记所有dfs过程中访问到的点,左边标记的点+右边未标记的点就是这个图的一个点覆盖。因为对于任意一条边,如果他的左边没标记,右边被标记了,那么我们就可找到一条新的增广路,所以每一条边都至少被一个点覆盖。再来证明:最大匹配=左边标记的点+右边未标记的点。对于每条匹配边,只有一个点属于点覆盖。如果这条边在dfs过程中被访问了,那么就左端点属于点覆盖,右端点不属于,否则就有左端点不属于点覆盖,右端点属于点覆盖。除此之外,不可能存在其它的点属于最小覆盖了,不然就必然可以找到增广路。所以:左边标记的点+右边未标记的点=最大匹配,对于任意的二分图,我们总能在最大匹配的基础上构造出一组点数等于最大匹配的点覆盖,所以:最小点覆盖=最大匹配。三、二分图最小边覆盖定义:边覆盖是图中一些边的集合,且对于图中所有的点,至少有一条集合中的边与其相关联,边数最小的覆盖就是最小边覆盖。定理:最小边覆盖=图中点的个数-最大匹配。简单证明:先贪心的选一组最大匹配的边加入集合,对于剩下的每个未匹配的点,随便选一条与之关联的边加入集合,得到的集合就是最小边覆盖,所以有:最小边覆盖=最大匹配+图中点的个数-2*最大匹配=图中点的个数-最大匹配。四、二分图最大独立集定义:独立集是图中一些点的集合,且图中任意两点之间都不存在边,点数最大的就是最大独立集。定理:最大独立集=图中点的个数-最大匹配。简单证明:可以这样来理解,先把所有的点都加入集合,删除最少的点和与其关联的边使得剩下的点相互之间不存在边,我们就得到了最大独立集。所以有:最大独立集=图中点的个数-最小点覆盖=图中点的个数-最大匹配。五、有向无环图最小不相交路径覆盖定义:用最少的不相交路径覆盖所有顶点。定理:把原图中的每个点V拆成Vx和Vy,如果有一条有向边A->B,那么就加边Ax-By。这样就得到了一个二分图,最小路径覆盖=原图的节点数-新图最大匹配。简单证明:一开始每个点都独立的为一条路径,总共有n条不相交路径。我们每次在二分图里加一条边就相当于把两条路径合成了一条路径,因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。所以有:最小路径覆盖=原图的节点数-新图最大匹配。六、有向无环图最小可相交路径覆盖定义:用最小的可相交路径覆盖所有顶点。算法:先用floyd求出原图的传递闭包,即如果a到b有路,那么就加边a->b。然后就转化成了最小不相交路径覆盖问题。七、偏序集的最大反链定义:偏序集中的最大独立集。Dilworth定理:对于任意偏序集都有,最大独立集(最大反链)=最小链的划分(最小可相交路径覆盖)。通过Dilworth定理,我们就可以把偏序集的最大独立集问题转化为最小可相交路径覆盖问题了。八、二分图带权最大匹配定义:每个边都有一组权值,边权之和最大的匹配就是带权最大匹配。算法:KM算法,复杂度为O(V^3)。具体就不说了,网上有不少资料。要注意的是,KM算法求的是最佳匹配,即在匹配是完备的基础上权值之和最大。这和带权最大匹配是不一样的,不过我们可以加入若干条边权为0的边使得KM求出来的最佳匹配等于最大权匹配。具体实现的时候最好用矩阵来存图,因为一般点的个数都是10^2级别,并且这样默认任意两点之间都存在边权为0的边,写起来很方便。如果要求最小权匹配,我们可以用一个很大数减去每条边的边权。二分图的最大团
定义:对于一般图来说,团是一个顶点集合,且由该顶点集合诱导的子图是一个完全图,简单说,就是选出一些顶点,这些顶点两两之间都有边。最大团就是使得选出的这个顶点集合最大。对于二分图来说,我们默认为左边的所有点之间都有边,右边的所有顶点之间都有边。那么,实际上,我们是要在左边找到一个顶点子集X,在右边找到一个顶点子集Y,使得X中每个顶点和Y中每个顶点之间都有边。
方法:二分图的最大团=补图的最大独立集。
扫描线(矩阵面积并)
double x[maxn * 2]; struct Edge { double l, r, h; int flag; } e[maxn * 2]; int sgn(double x) { return x < -eps? -1 : x < eps ? 0 : 1; } struct Tre { int l, r; int s; double len; } tre[maxn << 3]; //每个矩形两条边 所以需要8倍 bool cmp(const Edge &x, const Edge &y) { return x.h < y.h; } void pushup(int id) { if(tre[id].s) tre[id].len = x[tre[id].r+1] - x[tre[id].l]; else if(tre[id].l == tre[id].r) tre[id].len = 0; else { tre[id].len = tre[id<<1].len + tre[id<<1|1].len; } } void build(int id, int l, int r) { tre[id].l = l, tre[id].r = r, tre[id].s = 0, tre[id].len = 0; if (l == r) return; else { int mid = (l+r) >> 1; build(id<<1, l, mid); build(id<<1|1, mid+1, r); } } void update(int id, int l, int r, int val) { if (tre[id].l == l && r == tre[id].r) { tre[id].s = tre[id].s + val; pushup(id); return; } int mid = (tre[id].l+tre[id].r) >> 1; if (r <= mid) update(id<<1, l, r, val); else if (l > mid) update(id<<1|1, l, r, val); else { update(id<<1, l, mid, val); update(id<<1|1, mid + 1, r, val); } pushup(id); } void solve() { int n, cas = 1; while (cin >> n) { if (!n) break; int cnt = 0; for (int i = 0; i < n; i++) { db x1, x2, y1, y2; scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2); //x1, y1是矩形的左上角, x2, y2是矩形的右下角 Edge &t1 = e[cnt]; Edge &t2 = e[cnt+1]; t1.l = t2.l = x1; t1.r = t2.r = x2; t1.h = y1; t2.h = y2; t1.flag = 1; t2.flag = -1; x[cnt] = x1; x[cnt+1] = x2; cnt = cnt + 2; } sort(e, e + cnt, cmp); sort(x, x + cnt); int k = 1; for (int i = 1; i < cnt; i++) { if (sgn(x[i] - x[i-1]) != 0) { //离散化 x[k++] = x[i]; } } build(1, 0, k-1); double ans = 0.0; for (int i = 0; i < cnt; i++) { int l = lower_bound(x, x + k, e[i].l) - x; int r = lower_bound(x, x + k, e[i].r) - x - 1; update(1, l, r, e[i].flag); ans = ans + (e[i+1].h - e[i].h) * tre[1].len; } printf("Test case #%d\n", cas++); printf("Total explored area: %.2f\n\n", ans); } }