曾经做的一些小水题, 总之我不会做就对了.
这是第1篇.
TCO 2014 Semifinal 2 Div.1 口胡报告.
300 DP. 懒得写了.
500 $N$个点的无向图有条边$M$, 现在必须给边定向, 有一些从$S$到$T$的询问, 问最多能同时满足多少个询问.
边双内部的询问肯定是可以满足的.
因为图规模很小, 我们把所有桥压成一个状态. 对于每个询问, 考虑它经过的桥指向哪里, 在正向或反向的状态里压入这个桥.
然后$2^q$枚举询问, 考虑是否矛盾, 更新答案即可.
RoadsReform.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 #include <bits/stdc++.h> using namespace std ;const int maxn = 60 ;typedef long long ll;struct edge { int to, nxt; }e[maxn * 200 ]; int ptr = 1 , lnk[maxn], dfn[maxn], low[maxn], bel[maxn];int cnt, num, cut[maxn * 200 ], allq, lim, ans;ll fow[maxn], bkw[maxn]; int pre[maxn], tot, pth[maxn], vis[maxn];vector <pair<int , int > > brg;inline void add (int bgn, int end) { e[++ptr] = (edge){end, lnk[bgn]}; lnk[bgn] = ptr; } void tarjan (int x, int f) { low[x] = dfn[x] = ++cnt; for (int p = lnk[x]; p; p = e[p].nxt) { int y = e[p].to; if (!dfn[y]) { tarjan(y, x); low[x] = min(low[x], low[y]); if (low[y] > dfn[x]) cut[p] = cut[p ^ 1 ] = 1 , brg.push_back(make_pair(e[p].to, e[p ^ 1 ].to)); } else if (y != f) { low[x] = min(low[x], dfn[y]); } } } void dfs (int x) { bel[x] = num; for (int p = lnk[x]; p; p = e[p].nxt) { if (cut[p] || bel[e[p].to]) continue ; dfs(e[p].to); } } class RoadsReform {public : int getMax (int n, vector <int > e1, vector <int > e2, vector <int > s, vector <int > t) ;}; int getMax (int n, vector <int > e1, vector <int > e2, vector <int > s, vector <int > t) { for (vector <int >::size_type i = 0 ; i < e1.size(); ++i) { add(e1[i], e2[i]); add(e2[i], e1[i]); } tarjan(1 , 0 ); for (int i = 1 ; i <= n; ++i) if (!bel[i]) { num++; dfs(i); } allq = s.size(); queue <int > q; for (int i = 0 ; i < allq; ++i) { int fr = s[i], to = t[i]; if (bel[fr] == bel[to]) continue ; tot = 0 ; memset (vis, 0 , sizeof vis); q.push(fr); vis[fr] = 1 ; while (!q.empty()) { int u = q.front(); q.pop(); for (int p = lnk[u]; p; p = e[p].nxt) { int y = e[p].to; if (vis[y]) continue ; q.push(y); pre[y] = u; vis[y] = 1 ; } } for (int p = to; p != fr; p = pre[p]) pth[++tot] = p; pth[++tot] = fr; for (vector <int >::size_type v = 0 ; v < brg.size(); ++v) { for (int j = 1 ; j < tot; ++j) { if (brg[v].first == pth[j] && brg[v].second == pth[j + 1 ]) fow[i] |= (1l l << v); if (brg[v].second == pth[j] && brg[v].first == pth[j + 1 ]) bkw[i] |= (1l l << v); } } } lim = (1 << allq) - 1 ; for (int i = 0 ; i <= lim; ++i) { ll fs = 0 , bs = 0 ; int ret = 0 ; for (int j = 0 ; j < allq; ++j) { if (i & (1 << j)) fs |= fow[j], bs |= bkw[j], ret++; } if (fs & bs) continue ; ans = max(ans, ret); } return ans; }
850 有一棵树, 一开始只有一个点, 每轮在每个点上挂一个点, 问$N$轮操作后, 长度为$D$的路径有多少, 对质数$P$取模.
$N \le 10^9, M \le 500$.
orz jcvb.
注意到每轮操作后树必然是关于一条边对称的. 于是我们可以把他分解成上一轮的两个问题. 设$g_{n,d}$ 为$n$轮以一对称边端点为起点的长为$d$的路径条数.
那么我们有: $g_{n,d} = g_{n - 1, d - 1} + g_{n - 1, d}$, 也就是从自己这边的子问题$d$和用一条边接过来的子问题$d-1$来更新答案.
那么总的答案为: $f_{n,d} = \sum_{i+j=d-1}{g_{n - 1, i}g_{n - 1, j}} + 2f_{n - 1, d}$, 也就是考虑原来存在的答案和新增的答案.
对这两个问题列生成函数. 我们发现$g$就是组合数的形式. 所以$g_n(x) = (1+x)^n$.
那么原来$g$中两项卷起来的系数加到了$f$中$i+j+1$的系数中了, 也就是:
$f_n(x) = x(1+x)^{2n-2} + 2f_{n-1}(x)$.
解出: $\frac{2^nx-x(1+x)^{2n}}{1 - 2x - x^2} + 2^n$.
多项式卷积求逆都可以$O(D\log{D})$做, 不过不用那么麻烦, $D^2$也可以.
jcvb有$O(D)$的神仙做法, 我不会.
TwiceTwiceTree.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <bits/stdc++.h> using namespace std ;typedef long long ll;const int maxn = 1000 ;ll f[maxn], finv[maxn], inv[maxn]; ll mod; inline ll C (const int &N, const int &M) { ll ret = 1 ; for (int i = 1 ; i <= M; ++i) ret = (ret * (N - i + 1 )) % mod, ret = (ret * inv[i]) % mod; return ret; } inline ll quick_power (ll base, ll index) { ll ret = 1 ; while (index) { if (index & 1 ) ret = ret * base % mod; base = base * base % mod; index >>= 1 ; } return ret; } class TwiceTwiceTree {public : int sumup (int , int , int ) ; }; int TwiceTwiceTree::sumup(int N, int D, int P) { int n = N; mod = P; inv[1 ] = 1 ; for (int i = 2 ; i <= D; ++i) inv[i] = (mod - mod / i) * inv[mod % i] % mod; f[0 ] = 1 ; f[1 ] = mod - 2 ; f[2 ] = mod - 1 ; finv[0 ] = 1 ; for (int i = 1 ; i <= D; ++i) { ll ret = 0 ; for (int j = 1 ; j <= i; ++j) ret = (ret + f[j] * finv[i - j]) % mod; finv[i] = mod - ret; } int ans = 0 , poww = quick_power(2 , n); for (int i = 1 ; i <= D; ++i) { ll ret = mod - C(2 * n, i - 1 ); if (i == 1 ) ret = (ret + poww) % mod; ret = (ret * finv[D - i]) % mod; ans = (ans + ret) % mod; } return D ? ans : poww; }`