决定从头到尾干一波BZOJ!
可能会写没几题就停下吧,但还是想学学新姿势啦。
即求 $(1, 1)$ 到 $(n, m)$ 的最小割。跑 dinic 即可。
#include <bits/stdc++.h> using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } return x * f; } const int N = 1e6 + 10; const int INF = 0x3f3f3f3f; struct E { int v, ne, f; } e[N * 7]; int head[N], cnt, n, m, iter[N], level[N]; inline void add(int u, int v, int f) { e[cnt].v = v; e[cnt].f = f; e[cnt].ne = head[u]; head[u] = cnt++; e[cnt].v = u; e[cnt].f = f; e[cnt].ne = head[v]; head[v] = cnt++; } bool bfs(int s, int t) { for (int i = 0; i <= t; i++) level[i] = -1, iter[i] = head[i]; queue<int> que; que.push(s); level[s] = 0; while (!que.empty()) { int u = que.front(); que.pop(); for (int i = head[u]; ~i; i = e[i].ne) { int v = e[i].v, f = e[i].f; if (level[v] < 0 && f) { level[v] = level[u] + 1; que.push(v); } } } return level[t] != -1; } int dfs(int u, int t, int f) { if (u == t || !f) return f; int flow = 0; for (int i = iter[u]; ~i; i = e[i].ne) { iter[u] = i; int v = e[i].v; if (level[v] == level[u] + 1 && e[i].f) { int w = dfs(v, t, min(f, e[i].f)); if (!w) continue; e[i].f -= w, e[i^1].f += w; flow += w, f -= w; if (f <= 0) break; } } return flow; } int main() { memset(head, -1, sizeof(head)); n = read(), m = read(); for (int i = 1; i <= n; i++) { for (int j = 1; j < m; j++) { int f = read(); add((i - 1) * m + j, (i - 1) * m + j + 1, f); } } for (int i = 1; i < n; i++) { for (int j = 1; j <= m; j++) { int f = read(); add((i - 1) * m + j, i * m + j, f); } } for (int i = 1; i < n; i++) { for (int j = 1; j < m; j++) { int f = read(); add((i - 1) * m + j, i * m + j + 1, f); } } int ans = 0; int s = 1, t = n * m; for (; bfs(s, t); ans += dfs(s, t, INF)); printf("%d\n", ans); return 0; }
[FJOI2007]轮状病毒
生成树计数。
基尔霍夫矩阵为度数矩阵减去邻接矩阵。
无向图生成树计数为基尔霍夫矩阵的行列式
可得递推方程
$ans = 3 \times f(n - 1) - 2 \times f(n - 2) - 2$
$f(n) = 3 \times f(n - 1) - f(n - 2)$
加上高精度即可。
注意算行列式时多写几行容易看。
#include <bits/stdc++.h> using namespace std; struct Bigi { int a[600], len; Bigi() { memset(a, 0, sizeof(a)); len = 1; } friend Bigi operator * (int x, Bigi b) { Bigi res; res.len = b.len + 10; for (int i = 1; i <= res.len; i++) { res.a[i] += b.a[i] * x; res.a[i + 1] += res.a[i] / 10; res.a[i] %= 10; } while (res.len > 1 && res.a[res.len] == 0) res.len--; return res; } friend Bigi operator - (Bigi x, Bigi y) { Bigi res; res.len = x.len; for (int i = 1; i <= res.len; i++) { res.a[i] = x.a[i] - y.a[i]; while (res.a[i] < 0) { res.a[i] += 10; x.a[i + 1]--; } } while (res.len > 1 && res.a[res.len] == 0) res.len--; return res; } friend Bigi operator + (Bigi x, int y) { Bigi res = x; res.a[1] += y; for (int i = 1; i <= res.len; i++) { if (res.a[i] >= 10) { res.a[i] -= 10; res.a[i + 1]++; } else { break; } } while (res.a[res.len + 1]) res.len++; return res; } void print() { for (int i = len; i; i--) printf("%d", a[i]); puts(""); } } big[150], ans; int main() { int n; scanf("%d", &n); big[1] = big[1] + 3; big[2] = big[2] + 8; if (n <= 2) { big[n].print(); return 0; } for (int i = 3; i <= n; i++) big[i] = 3 * big[i - 1] - big[i - 2]; ans = 3 * big[n - 1] - 2 * big[n - 2]; ans = ans + (-2); ans.print(); return 0; }
[ZJOI2006]物流运输
$cost[i][j]$ 表示第 $i$ 天到第 $j$ 天都走同一条路线时每天的最小花费,即为 $1$ 到 $m$ 的最短路。dijkstra即可。
然后 $dp[i]$ 表示到第 $i$ 天的最小花费
$dp[i] = min(dp[j] + cost[j + 1][i] * (i - j) + k)$
#include <bits/stdc++.h> #define pii pair<int, int> #define fi first #define se second using namespace std; const int INF = 10000000; const int N = 110; int cost[N][N], dp[N], n, m, k, e; vector<pii> G[N]; bool ban[N][N], unable[N], done[N]; int dis[N]; inline void checkmin(int &a, int b) { if (a > b) a = b; } int dijkstra(int l, int r) { for (int i = 1; i <= m; i++) { unable[i] = 0; done[i] = 0; dis[i] = INF; for (int j = l; j <= r; j++) unable[i] |= ban[j][i]; } if (unable[1] || unable[m]) return INF; priority_queue<pii, vector<pii>, greater<pii> > que; dis[1] = 0; que.push(pii(0, 1)); while (!que.empty()) { auto pp = que.top(); que.pop(); int u = pp.se; if (done[u]) continue; done[u] = 1; for (auto p: G[u]) { int v = p.se, c = p.fi; if (!unable[v] && dis[v] > dis[u] + c) { dis[v] = dis[u] + c; que.push(pii(dis[v], v)); } } } return dis[m]; } int main() { //freopen("in.txt", "r", stdin); scanf("%d%d%d%d", &n, &m, &k, &e); for (int u, v, c; e--; ) { scanf("%d%d%d", &u, &v, &c); G[u].push_back(pii(c, v)); G[v].push_back(pii(c, u)); } int q; scanf("%d", &q); for (int u, a, b; q--; ) { scanf("%d%d%d", &u, &a, &b); for (int i = a; i <= b; i++) ban[i][u] = 1; } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) cost[i][j] = dijkstra(i, j); for (int i = 1; i <= n; i++) { dp[i] = cost[1][i] * i; for (int j = 1; j < i; j++) checkmin(dp[i], dp[j] + cost[j + 1][i] * (i - j) + k); } printf("%d\n", dp[n]); return 0; }
[HNOI2008]明明的烦恼
prufer序列为无根树的一种数列。长度为 $n$ - $2$
prufer转无根树
将最小编号的叶子删去,prufer序列加入其父亲。重复至树只剩下两个节点。
无根树转prufer
取出prufer首元素,与待选点集中最小未出现在prufer序列中的点连边,并将该点在待选点集中删去,直至待选点集剩下两个节点,将这两个节点连边。待选点集初始为 $1$ ~ $n$。
一个节点在prufer序列中出现次数为该节点度数减一。
判断无解的情况:出现度数为 $0$ 的点,在prufer序列中出现次数超过 $n$ - $2$。
有解情况下,设 $cnt$ 为有度数要求的节点个数,$sum = \sum_{i = 1} ^{cnt}(d_i - 1)$。
那么答案为 $C_{n-2}^{sum} \times \dfrac{sum!}{\prod_{i=1}^{cnt}(d_i-1)!} \times (n-cnt)^{n-2-sum}$
化简得到$\dfrac{(n-2)!}{(n-sum-2)! \times \prod_{i=1}^{cnt}(d_i-1)!} \times (n-cnt)^{n-2-sum}$
#include <bits/stdc++.h> const int N = 1007; const int MOD = 10000; int num[N]; int prime[N], tol, d[N], c[N]; bool vis[N]; void init() { for (int i = 2; i < N; i++) { if (!vis[i]) prime[++tol] = i; for (int j = 1; j <= tol && i * prime[j] < N; j++) { vis[i * prime[j]] = 1; if (i % prime[j] == 0) break; } } } void add(int x, int o) { for (int i = 1; i <= tol; i++) { while (x % prime[i] == 0) c[i] += o, x /= prime[i]; } } int main() { init(); int n; scanf("%d", &n); bool flag = 0; int sum = 0, cnt = 0; for (int i = 1; i <= n; i++) { scanf("%d", d + i); if (!d[i] || d[i] > n - 1) flag = 1; if (d[i] != -1) sum += d[i] - 1, cnt++; } if (n == 1) { if (!d[1]) puts("1"); else puts("0"); return 0; } if (sum > n - 2 || flag) { puts("0"); return 0; } for (int i = n - 2 - sum + 1; i <= n - 2; i++) add(i, 1); for (int i = 1; i <= n; i++) { if (d[i] > -1) { for (int j = 2; j < d[i]; j++) add(j, -1); } } int len = 0; num[++len] = 1; for (int i = 1; i <= n - 2 - sum; i++) { for (int j = 1; j <= len; j++) num[j] *= n - cnt; for (int j = 1; j <= len; j++) { if (num[j] >= MOD) { num[j + 1] += num[j] / MOD; num[j] %= MOD; } } while (num[len + 1]) { num[len + 1] += num[len] / MOD; num[len] %= MOD; len++; } } for (int i = 1; i <= tol; i++) { while (c[i]) { for (int j = 1; j <= len; j++) num[j] *= prime[i]; for (int j = 1; j <= len; j++) { if (num[j] >= MOD) { num[j + 1] += num[j] / MOD; num[j] %= MOD; } } while (num[len + 1]) { num[len + 1] += num[len] / MOD; num[len] %= MOD; len++; } c[i]--; } } printf("%d", num[len]); for (int i = len - 1; i; i--) printf("%04d", num[i]); puts(""); return 0; }
[HNOI2008]水平可见直线
可见的直线为一下凸壳。
先按斜率和截距从小到大排序,再用单调栈判断交点的相对位置即可。
#include <bits/stdc++.h> const int N = 5e4 + 7; const double eps = 1e-7; inline int dcmp(double x) { if (fabs(x) < eps) return 0; return x < 0 ? -1 : 1; } struct P { double x, y; int id; inline bool operator < (const P &rhs) const { if (dcmp(x - rhs.x) == 0) return y < rhs.y; return x < rhs.x; } } p[N]; int st[N], top; inline double crossx(const P &a, const P &b) { return (a.y - b.y) / (b.x - a.x); } void ins(int id) { const P &cur = p[id]; while (top) { if (dcmp(p[st[top]].x - cur.x) == 0) top--; else if (top > 1 && dcmp(crossx(p[st[top]], cur) - crossx(p[st[top]], p[st[top - 1]])) <= 0) top--; else break; } st[++top] = id; } int ans[N]; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%lf%lf", &p[i].x, &p[i].y); p[i].id = i; } std::sort(p + 1, p + 1 + n); for (int i = 1; i <= n; i++) ins(i); for (int i = 1; i <= top; i++) ans[p[st[i]].id] = 1; for (int i = 1; i <= n; i++) if (ans[i]) printf("%d ", i); return 0; }
[HNOI2008]越狱
总方案数 - 相邻颜色均不同的方案数。
#include <bits/stdc++.h> #define ll long long const int MOD = 100003; int qp(int a, ll b) { a %= MOD; int ans = 1; while (b) { if (b & 1) ans = 1LL * ans * a % MOD; a = 1LL * a * a % MOD; b >>= 1; } return ans; } int main() { int m; ll n; scanf("%d%lld", &m, &n); int ans = qp(m, n); ans = (ans - 1LL * m * qp(m - 1, n - 1) % MOD) % MOD; ans = (ans + MOD) % MOD; printf("%d\n", ans); return 0; }