T1 HNOI2015 实验比较
给 $n$ 个有权值的物品,$m$ 条消息,消息可以是“小于”或者“等于”,一个物品只会与一个小于等于它的东西比较,求最后权值排名方案数 mod 998244353
$n \leq 500$
sol:
考场上自闭了,考出来更自闭
相等的节点缩起来,是一个森林,你要做的是给这个森林编号,要求儿子的编号小于父亲的,求方案数
然后 dp 就完事了,$f_{(x,i)}$ 表示 $x$ 为根的子树划分成 $i$ 个编号的方案数,合并两个子树实质上是合并两个编号序列搞个 $g_{(i,j,k)}$ 表示把 $i$ 个编号和 $j$ 个编号的序列合成 $k$ 个编号的序列的方案数,一起转移即可
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <bits/stdc++.h> #define LL long long #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i) using namespace std; inline int read() { int x = 0, f = 1; char ch; for (ch = getchar(); !isdigit(ch); ch = getchar()) if (ch == '-') f = -f; for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0'; return x * f; } int n, m; const int mod = 1e9 + 7, maxn = 510; int fa[maxn], u[maxn], v[maxn], ind[maxn]; inline int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);} int first[maxn], to[maxn << 1], nx[maxn << 1], cnt; inline void add(int u, int v) {if(u == v) return;to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt; ind[v]++;} int vis[maxn], size[maxn]; LL d[maxn][maxn][maxn], dp[maxn][maxn]; void dfs(int x) { dp[x][0] = 1; LL tmp[maxn]; memset(tmp, 0, sizeof(tmp)); for(int i=first[x];i;i=nx[i]) { if(to[i] == x) continue; dfs(to[i]); dwn(j, size[x], 0) rep(k, 1, size[to[i]]) rep(l, max(j, k), (j + k)) (tmp[l] += (1LL * (1LL * dp[x][j] * dp[to[i]][k] % mod) * d[j][k][l] % mod)) %= mod; size[x] += size[to[i]]; rep(j, 0, size[x]) dp[x][j] = tmp[j], tmp[j] = 0; } size[x]++; dwn(i, size[x], 1) dp[x][i] = dp[x][i - 1]; } int find_circle(int x) { if(vis[x] == -1) return 1; vis[x] = -1; for(int i=first[x];i;i=nx[i]) if(!vis[to[i]] && find_circle(to[i])) return 1; else if(vis[to[i]] == -1) return 1; vis[x] = 1; return 0; } int main() { //freopen("sort.in","r",stdin); //freopen("sort.out","w",stdout); n = read(), m = read(); rep(i, 1, n) fa[i] = i; char ch[2]; rep(i, 1, m) { scanf("%d%s%d", &u[i], ch, &v[i]); if(ch[0] == '=') fa[find(u[i])] = find(v[i]); } rep(i, 1, m) add(find(u[i]), find(v[i])); rep(i, 1, n) if(!vis[find(i)] && find_circle(find(i))) {cout << 0 << endl; return 0;} d[0][0][0] = 1; rep(i, 0, n+1) rep(j, 0, n+1) rep(k, max(i, j), (i + j)) { if((!i) && (!j)) continue; if(i) (d[i][j][k] += d[i - 1][j][k - 1]) %= mod; if(j) (d[i][j][k] += d[i][j - 1][k - 1]) %= mod; if(i && j) (d[i][j][k] += d[i - 1][j - 1][k - 1]) %= mod; //cout << d[i][j][k] << endl; } //cout << d[2][3][4] << endl; rep(i, 1, n) if(!ind[find(i)]) add(n + 1, find(i)); dfs(n + 1); LL ans = 0; rep(i, 1, n+1) (ans += dp[n+1][i]) %= mod; cout << ans << endl; }
T2 bzoj3681 Arietta
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <bits/stdc++.h> #define LL long long #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i) using namespace std; inline int read() { int x = 0, f = 1; char ch; for (ch = getchar(); !isdigit(ch); ch = getchar()) if (ch == '-') f = -f; for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0'; return x * f; } const int maxn = 100010, maxm = 1000010, oo = 2147483233; struct Dinic { int cur[maxm], head[maxm], nx[maxm]; int n, m, s, t; struct Edge { int from, to, caps; Edge(){} Edge(int _1, int _2, int _3): from(_1), to(_2), caps(_3) {} }es[maxm]; void AddEdge(int u, int v, int w) { es[m] = Edge(u, v, w); nx[m] = head[u]; head[u] = m++; es[m] = Edge(v, u, 0); nx[m] = head[v]; head[v] = m++; } void setn(int _) {n = _;} Dinic() {m = 0; memset(head, -1, sizeof(head)); clo = 0;} queue<int> q; int dis[maxm], vis[maxm], clo; bool BFS() { //rep(i, 0, n) dis[i] = 0; q.push(t); vis[t] = ++clo; dis[t] = 0; while(!q.empty()) { int now = q.front(); q.pop(); cur[now] = head[now]; for(int i=head[now];~i;i=nx[i]) { Edge &e = es[i^1]; if((vis[e.from] != clo) && e.caps) { vis[e.from] = clo; dis[e.from] = dis[now] + 1; q.push(e.from); } } } return (vis[s] == clo); } int DFS(int u, int a) { if(u == t || !a) return a; int flow = 0, f; for(int &i=cur[u];~i;i=nx[i]) { Edge &e = es[i]; if(dis[e.to] == dis[u] - 1 && (f = DFS(e.to, min(e.caps, a)))) { e.caps -= f; es[i^1].caps += f; a -= f; flow += f; if(!a) return flow; } } return flow; } int MaxFlow(int _s, int _t) { s = _s, t = _t; int flow = 0; while(BFS()) { flow += DFS(s, 2147483233); } return flow; } } sol; int s, t, nodes; int n, m; int fa[maxn], v[maxn]; int first[maxn], nx[maxn], to[maxn], cnt; inline void add(int u, int v) { to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt; } int root[maxn], ls[maxm << 1], rs[maxm << 1], dfn; inline void Insert(int &x, int l, int r, int pos, int p) { x = ++dfn; if(l == r) { sol.AddEdge(p, x + nodes, oo); return; } int mid = (l + r) >> 1; if(pos <= mid) Insert(ls[x], l, mid, pos, p) ; else Insert(rs[x], mid + 1, r, pos, p) ; if(ls[x]) sol.AddEdge(ls[x] + nodes, x + nodes, oo); if(rs[x]) sol.AddEdge(rs[x] + nodes, x + nodes, oo); } inline int merge(int x, int y, int l, int r) { if(!x || !y) return x + y; int z = ++dfn; if(l == r) { sol.AddEdge(x + nodes, z + nodes, oo); sol.AddEdge(y + nodes, z + nodes, oo); return z; } int mid = (l + r) >> 1; ls[z] = merge(ls[x], ls[y], l, mid); rs[z] = merge(rs[x], rs[y], mid+1, r); if(ls[z]) sol.AddEdge(ls[z] + nodes, z + nodes, oo); if(rs[z]) sol.AddEdge(rs[z] + nodes, z + nodes, oo); return z; } inline void AddEDG(int x, int l, int r, int L, int R, int p) { if(!x) return; if(L <= l && r <= R) { sol.AddEdge(x + nodes, p, oo); return; } int mid = (l + r) >> 1; if(L <= mid) AddEDG(ls[x], l, mid, L, R, p); if(R > mid) AddEDG(rs[x], mid+1, r, L, R, p); } inline void dfs(int x) { Insert(root[x], 1, n, v[x], x); for(int i=first[x];i;i=nx[i]) { dfs(to[i]); root[x] = merge(root[x], root[to[i]], 1, n); } } int main() { n = read(), m = read(); s = n + m + 1, t = n + m + 2, nodes = t + 1; rep(i, 2, n) { fa[i] = read(); add(fa[i], i); } rep(i, 1, n) { v[i] = read(); sol.AddEdge(s, i, 1); } dfs(1); rep(i, 1, m) { int l = read(), r = read(), k = read(), ct = read(); AddEDG(root[k], 1, n, l, r, i+n); sol.AddEdge(i + n, t, ct); } sol.setn(nodes + dfn + 5); cout << sol.MaxFlow(s, t) << endl; }
T3 bzoj3772 精神污染
给一棵树,给 $m$ 条路径,随机选两条编号不同的,求一条包含另一条的概率
$n,m \leq 100000$
sol:
考场上想的是开个 vector 记录每个左端点的右端点都是啥,然后对欧拉序建主席树,每次把当前点挂的所有右端点差分成入栈 +1,出栈 -1
然后枚举每一条路径,定位到每条路径对应的线段树,分别查 $x->lca$,$y->lca$,$lca->lca$ 的和最后 $-1$ 就可以了(因为欧拉序好像不太滋磁直接查一条不是祖孙链的路径)
这个东西常数奇大,空间还卡,满数据 1.2s 而且没啥可优化的了
后来发现数两个矩形里的点就可以了
不过调 4K 的代码还是挺爽的
一下午写了 10K,去世
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <bits/stdc++.h> #define LL long long #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i) using namespace std; inline int read() { int x = 0, f = 1; char ch; for (ch = getchar(); !isdigit(ch); ch = getchar()) if (ch == '-') f = -f; for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0'; return x * f; } const int maxn = 530000; int n, m, u[maxn], v[maxn]; int first[maxn], to[maxn << 1], nx[maxn << 1], cnt; inline void add(int u, int v) { to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt; } int ind[maxn], oud[maxn], dep[maxn], anc[maxn][21], dfn; void dfs(int x) { ind[x] = ++dfn; rep(i, 1, 20) anc[x][i] = anc[anc[x][i - 1]][i - 1]; for(int i=first[x];i;i=nx[i]) { if(to[i] == anc[x][0]) continue; anc[to[i]][0] = x; dep[to[i]] = dep[x] + 1; dfs(to[i]); } oud[x] = dfn; } inline int get_son(int x, int y) { int t = dep[x] - dep[y] - 1; rep(i, 0, 20) if(t & (1 << i)) x = anc[x][i]; return x; } int cntp, cntq; struct Point { int x, y; Point(){}; Point(int u, int v) {x = u, y = v;} bool operator < (const Point &b) const { return x == b.x ? y < b.y : x < b.x; } bool operator == (const Point &b) const { return x == b.x && y == b.y; } }ps[maxn]; struct Ques { int x, l, r, opt; Ques(){} Ques(int _x, int _l, int _r, int _opt) {x = _x, l = _l, r = _r, opt = _opt;} bool operator < (const Ques &b) const { return x < b.x; } }qs[maxn]; void AddPoint(int x, int y) { if(x < y) swap(x, y); ps[++cntp] = Point(x, y); } void AddArea(int x, int y, int u, int v) { if(u > v || x > y) return; if(x < u) swap(x, u), swap(y, v); qs[++cntq] = Ques(x, u, v, 1); qs[++cntq] = Ques(y + 1, u, v, -1); } int c[maxn]; inline int lowbit(int x) {return x & (-x);} inline void update(int x, int opt) {for(;x <= n; x += lowbit(x)) c[x] += opt;} inline int cal(int x) {int res = 0; for(; x; x -= lowbit(x)) res += c[x]; return res;} int main() { //freopen("path.in","r",stdin); //freopen("path.out","w",stdout); n = read(), m = read(); rep(i, 2, n) { int u = read(), v = read(); add(u, v); add(v, u); } dfs(1); rep(i, 1, m) { int u = read(), v = read(); AddPoint(ind[u], ind[v]); if(dep[u] < dep[v]) swap(u, v); if(u == v) AddArea(ind[u], oud[u], 1, ind[u]), AddArea(ind[u], oud[u], oud[u] + 1, n); else if(ind[v] <= ind[u] && ind[u] <= oud[v]) { int cur = get_son(u, v); //cout << cur << endl; AddArea(ind[u], oud[u], 1, ind[cur] - 1); AddArea(ind[u], oud[u], oud[cur] + 1, n); } else AddArea(ind[u], oud[u], ind[v], oud[v]); } sort(ps + 1, ps + cntp + 1); sort(qs + 1, qs + cntq + 1); LL ans = 0; int cur = 1; rep(i, 1, cntp) { while(cur <= cntq && qs[cur].x <= ps[i].x) update(qs[cur].l, qs[cur].opt), update(qs[cur].r + 1, -qs[cur].opt), cur++; ans += cal(ps[i].y); } int jj; for(int i=1;i<=m;i=jj) { for(jj = i; ps[jj] == ps[i]; jj++); ans -= (jj - i) * (jj - i - 1) / 2; } //cout << ans << endl; ans -= m; LL ans2 = 1LL * m * (m - 1) / 2; LL g = __gcd(ans, ans2); ans /= g; ans2 /= g; long double fans = 1.00 * ans / ans2; double pans = fans; printf("%.6f\n", pans); }
upd:T2 T 成暴力分,T3 MLE
不过学习了网络流的优秀写法
T3 主席树常数太大过不了,需要扫描线树状数组
下面是考场写的 TLE + MLE 代码 qnq ↓
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <bits/stdc++.h> #define LL long long #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i) using namespace std; inline int read() { int x = 0, f = 1; char ch; for (ch = getchar(); !isdigit(ch); ch = getchar()) if (ch == '-') f = -f; for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0'; return x * f; } const int maxn = 550050; int n, m; int first[maxn], to[maxn << 1], nx[maxn << 1], cnt; inline void add(int u, int v) {to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt;} struct Ques { int l, r; bool operator < (const Ques &b) const { return (l == b.l) ? (r < b.r) : (l < b.l); } bool operator == (const Ques &b) const { return (l == b.l) && (r == b.r); } }qs[maxn]; struct Chaintable { int head[maxn], nx[maxn], val[maxn], cnt; Chaintable() {cnt = 0;} void AddItem(int u, int v) {val[++cnt] = v; nx[cnt] = head[u]; head[u] = cnt;} }ct; //vector<int> G[maxn]; int fa[maxn], dep[maxn], size[maxn], bl[maxn], ind[maxn], oud[maxn], mxs[maxn], dfn; void dfs1(int x) { size[x] = 1; ind[x] = ++dfn; for(int i=first[x];i;i=nx[i]) { if(to[i] == fa[x]) continue; fa[to[i]] = x; dep[to[i]] = dep[x] + 1; dfs1(to[i]); size[x] += size[to[i]]; if(!mxs[x] || size[to[i]] > size[mxs[x]]) mxs[x] = to[i]; } oud[x] = ++dfn; } void dfs2(int x, int col) { bl[x] = col; if(!mxs[x]) return; dfs2(mxs[x], col); for(int i=first[x];i;i=nx[i]) if(to[i] != fa[x] && to[i] != mxs[x]) dfs2(to[i], to[i]); } int lca(int x, int y) { while(bl[x] != bl[y]) { if(dep[bl[x]] < dep[bl[y]]) swap(x, y); x = fa[bl[x]]; } return dep[x] < dep[y] ? x : y; } int root[maxn], ls[maxn * 45], rs[maxn * 45], val[maxn * 45], ToT; void build(int &x, int l, int r) { x = ++ToT; if(l == r) return; int mid = (l + r) >> 1; build(ls[x], l, mid); build(rs[x], mid + 1, r); } void Insert(int &x, int pre, int l, int r, int pos, int opt) { x = ++ToT; val[x] = val[pre] + opt; if(l == r) return; int mid = (l + r) >> 1; ls[x] = ls[pre], rs[x] = rs[pre]; if(pos <= mid) Insert(ls[x], ls[pre], l, mid, pos, opt); else Insert(rs[x], rs[pre], mid+1, r, pos, opt); } int query(int x, int y, int lca, int flca, int l, int r, int L, int R) { if(L <= l && r <= R) return val[x] + val[y] - val[lca] - val[flca]; int mid = (l + r) >> 1, ans = 0; if(L <= mid) ans += query(ls[x], ls[y], ls[lca], ls[flca], l, mid, L, R); if(R > mid) ans += query(rs[x], rs[y], rs[lca], rs[flca], mid+1, r, L, R); return ans; } void dfs(int x) { root[x] = root[fa[x]]; for(int i=ct.head[x];i;i=ct.nx[i]) { int px = ct.val[i]; Insert(root[x], root[x], 1, dfn, ind[px], 1); Insert(root[x], root[x], 1, dfn, oud[px], -1); } for(int i=first[x];i;i=nx[i]) { if(to[i] == fa[x]) continue; dfs(to[i]); } } int main() { //freopen("path.in","r",stdin); //freopen("path.out","w",stdout); n = read(), m = read(); rep(i, 2, n) { int u = read(), v = read(); add(u, v); add(v, u); } dfs1(1); dfs2(1, 1); //build(root[0], 1, dfn); rep(i, 1, m) { int u = read(), v = read(); if(u > v) swap(u, v); //G[u].push_back(v); ct.AddItem(u, v); qs[i].l = u, qs[i].r = v; } dfs(1); LL ans1 = 0, ans2 = 0; sort(qs + 1, qs + m + 1); rep(i, 1, m) { int qx = qs[i].l, qy = qs[i].r, anc = lca(qx, qy); ans1 += query(root[qx], root[qy], root[anc], root[fa[anc]], 1, dfn, ind[anc], ind[qx]); ans1 += query(root[qx], root[qy], root[anc], root[fa[anc]], 1, dfn, ind[anc], ind[qy]); ans1 -= query(root[qx], root[qy], root[anc], root[fa[anc]], 1, dfn, ind[anc], ind[anc]); ans1 --; } ans2 = 1LL *m * (m - 1) / 2; int cur; for(int i=1;i<=m;i=cur) { for(cur = i; qs[cur] == qs[i]; cur++); ans1 -= 1LL * (cur - i) * (cur - i - 1) / 2; }/* rep(i, 1, m) { cout << qs[i].l << " " << qs[i].r << endl; }*/ LL GDC = __gcd(ans1, ans2); ans1 /= GDC, ans2 /= GDC; double ans = 1.00 * ans1 / ans2; printf("%.6f\n", ans); }