intmain(){ scanf("%d%d%d", &mine, &hp, &per); if (hp <= mine) {puts("1"); return0;} if (per >= mine * 2) {puts("-1"); return0;} if (per < mine) { hp -= mine; int round = 1; while (hp > mine) hp -= mine - per, round++; for (int i = 0; i <= hp; i++) { if (i * (mine - per) >= hp) { if (ans == -1 || (round + i) < ans) ans = round + i; continue; } int h = hp - i * (mine - per), n = per - (mine - h), my = mine - n; int now = round + i + 1; while (1) { now++; n -= my; if (n <= 0) break; my -= n; if (my <= 0) {now = -1; break;} } if (now != -1 && (ans == -1 || now < ans)) ans = now; } printf("%d\n", ans); return0; } hp -= mine; if (hp > mine) {puts("-1"); return0;} int n = per - mine + hp, round = 2; if (n >= mine) {puts("-1"); return0;} mine -= n; while (1) { round++; n -= mine; if (n <= 0) break; mine -= n; if (mine <= 0) {puts("-1"); return0;} } ans = round; printf("%d\n", ans); return0; }
TopCoder MegaFactorial
定义:
f(n,k)=⎩⎪⎪⎨⎪⎪⎧f(n,k−1)f(n−1,k)1nn>0,k>0n=0k=0
给定 n,k,b,求 f(n,k) 中 b 因子的个数。
首先 b 的因子里只有最大的质因数的幂次会成为答案,因此可以把 B 转化成最大的质因数的幂次,然后求出这个质因数的个数再除掉指数下取整即可得到答案。
然后考虑质数咋做,那就是枚举其幂次,然后对于每个枚举的 x,在 (kx,0) 处会多 1,那么连续的 x 行一组,矩阵快速幂优化 DP 即可。
int n, m, num, vis[1605][1605], cnt[1605], f[1605]; char ch[45][45]; queue<pair<int, int>> q; structdsu { int fa[1605]; dsu() {for (int i = 1; i < 1605; i++) fa[i] = i;} intfind(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);} voidmerge(int x, int y){fa[find(x)] = find(y); return ;} } d;
intgetid(int x, int y){return (x - 1) * m + y;} intpower(int a, int b){ int ans = 1; while (b) { if (b & 1) ans = (longlong)ans * a % mod; a = (longlong)a * a % mod; b >>= 1; } return ans % mod; } intcheckout(int i, int k){ int x = (i + m - 1) / m, y = i % m; if (!y) y = m; if (ch[x][y]== '#') return0; int xx = x + gx[k], yy = y + gy[k]; return xx < 1 || xx > n || yy < 1 || yy > m; } intcheckok(int i, int k){ int x = (i + m - 1) / m, y = i % m; if (!y) y = m; if (ch[x][y]== '#') return0; int xx = x + gx[k], yy = y + gy[k]; return1 <= xx && xx <= n && 1 <= yy && yy <= m; }
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) { string s; cin >> s; m = s.length(); s = " " + s; for (int j = 1; j <= m; j++) ch[i][j] = s[j], num += ch[i][j] == '.', f[getid(i, j)] = ch[i][j] == '.'; } // eprintf("%d\n", checkok(3, 2)); for (int i = 1; i <= n * m; i++) for (int j = 1; j <= n * m; j++) for (int k = 0; k < 4; k++) if (checkout(i, k) && checkok(j, k)) { q.emplace(i, j), vis[i][j] = 1; break; } while (!q.empty()) { int u = q.front().first, v = q.front().second; q.pop(); int ux = (u + m - 1) / m, uy = u % m, vx = (v + m - 1) / m, vy = v % m; if (!uy) uy = m; if (!vy) vy = m; // eprintf("%d %d\n", u, v); // eprintf("%d %d %d %d\n", ux, uy, vx, vy); for (int i = 0; i < 4; i++) { int uxx = ux + gx[i], uyy = uy + gy[i], vxx = vx + gx[i], vyy = vy + gy[i]; if (ch[uxx][uyy] != '.' || ch[vxx][vyy] != '.') continue; if (vis[getid(uxx, uyy)][getid(vxx, vyy)]) continue; q.emplace(getid(uxx, uyy), getid(vxx, vyy)); vis[getid(uxx, uyy)][getid(vxx, vyy)] = 1; } for (int i = 0; i < 4; i++) { int uxx = ux + gx[i], uyy = uy + gy[i], vxx = vx + gx[3 - i], vyy = vy + gy[3 - i]; if (ch[uxx][uyy] != '.' || ch[vxx][vyy] != '#') continue; if (vis[getid(uxx, uyy)][getid(vx, vy)]) continue; q.emplace(getid(uxx, uyy), getid(vx, vy)); vis[getid(uxx, uyy)][getid(vx, vy)] = 1; } for (int i = 0; i < 4; i++) { int uxx = ux + gx[i], uyy = uy + gy[i], vxx = vx + gx[3 - i], vyy = vy + gy[3 - i]; if (ch[uxx][uyy] != '#' || ch[vxx][vyy] != '.') continue; if (vis[getid(ux, uy)][getid(vxx, vyy)]) continue; q.emplace(getid(ux, uy), getid(vxx, vyy)); vis[getid(ux, uy)][getid(vxx, vyy)] = 1; } } for (int i = 1; i <= n * m; i++) if (f[i]) for (int j = i + 1; j <= n * m; j++) if (f[j]) if (!vis[i][j] && !vis[j][i]) d.merge(i, j); for (int i = 1; i <= n * m; i++) if (f[i]) cnt[d.find(i)]++; int ans = (power(2, num) + mod - 1) % mod; // eprintf("%d\n", ans); // for (int i = 1; i <= n * m; i++) // for (int j = i; j <= i; j++) // if (vis[i][i]) eputs("?"); for (int i = 1; i <= n * m; i++) if (cnt[i]) ans = (ans + mod - power(2, cnt[i]) + 1) % mod; printf("%d\n", ans); return0; }
int n, W, h[15], l[15], r[15]; map<string, int> mp[15];
string encode(int *mxh){ string s = ""; for (int i = 1; i <= W; i++) s += to_string(mxh[i]) + "#"; return s; } voiddecode(string s, int *mxh){ int val = 0, idx = 0; for (char i : s) if (isdigit(i)) val = val * 10 + i - '0'; else mxh[++idx] = val, val = 0; return ; } intcheck(int *mxh, int pos, int h, int l, int r){ int le = 1e9, ri = 0; for (int j = 1; j <= W; j++) if (h - abs(pos - j) > mxh[j]) le = min(le, j), ri = max(ri, j); if (!l && le > ri) return1; return le == l && ri == r; } intdfs(string s, int now){ if (now == 0) return1; if (mp[now].count(s)) return mp[now][s]; int mxh[55]; decode(s, mxh); // if (now == 2) { // for (int i = 1; i <= W; i++) eprintf("%d ", mxh[i]); // eputs(""); // } // if (!l[now]) return mp[now][s] = (long long)W * dfs(s, now - 1) % mod; int res = 0; for (int i = 1; i <= W; i++) if (check(mxh, i, h[now], l[now], r[now])) { // if (now == 3) eputs("?"); int nxth[55]; for (int j = 1; j <= W; j++) nxth[j] = max(mxh[j], h[now] - abs(i - j)); res = (res + dfs(encode(nxth), now - 1)) % mod; } return mp[now][s] = res; }
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &h[i]); scanf("%d", &n); for (int i = 1; i <= n; i++) { string s; cin >> s; W = s.length(); s = " " + s; for(int j = 1; j <= W; j++) if (s[j] == 'X') { if (!l[i]) l[i] = j; r[i] = j; } } string s = ""; for (int i = 1; i <= W; i++) s += "0#"; printf("%d\n", dfs(s, n)); return0; }
TopCoder Tunnels
题面懒得写了。
答辩题,从上往下考虑,将上面的已有路径与下面匹配,设 dpi,a,b 表示前 i 行,左边有 a 条能与下面匹配,右边有 b 条时最大的匹配数,分讨转移即可。
int gx[] = {1, -1, 0, 0}, gy[] = {0, 0, -1, 1}; int n, m, cnt, id[55][55]; char ch[55][55]; int deg[55][55], mn[2505], mx[2505]; int lu[55], ld[55], ru[55], rd[55], lr[55]; int dp[55][55][55], ans;
intdfs(int x, int y, int c){ if (x < 1 || x > n || y < 1 || y > m || ch[x][y] == '.') return0; if (id[x][y]) return1; id[x][y] = c; mn[c] = min(mn[c], x); mx[c] = max(mx[c], x); for (int d = 0; d < 4; d++) deg[x][y] += dfs(x + gx[d], y + gy[d], c); return1; }
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) { string s; cin >> s; m = s.length(); s = " " + s; for (int j = 1; j <= m; j++) ch[i][j] = s[j]; } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) if (ch[i][j] == 'X' && !id[i][j]) mn[++cnt] = n + 1, dfs(i, j, cnt); if (cnt == 0) {puts("0"); return0;} if (m == 1) {puts("1"); return0;} for (int i = 1; i <= n; i++) { if (ch[i][1] == 'X' && deg[i][1] <= 1) { if (i != 1 && i == mn[id[i][1]]) lu[i] = 1; elseif (i == mx[id[i][1]]) ld[i] = 1; } if (ch[i][m] == 'X' && deg[i][m] <= 1) { if (i != 1 && i == mn[id[i][m]]) ru[i] = 1; elseif (i == mx[id[i][m]]) rd[i] = 1; } lr[i] = mn[id[i][1]] == i && mx[id[i][1]] == i; for (int j = 1; j <= m; j++) lr[i] &= ch[i][j] == 'X'; if (lr[i]) lu[i] = ru[i] = ld[i] = rd[i] = 1; } for (int i = 1; i < n; i++) { if (lu[i] && ld[i + 1]) ld[i + 1] = 0; if (ru[i] && rd[i + 1]) rd[i + 1] = 0; } memset(dp, ~0x3f, sizeof(dp)); ans = dp[0][0][0]; dp[0][0][0] = 0; for (int i = 1; i <= n; i++) for (int a = 0; a <= i; a++) for (int b = 0; b <= i; b++) { for (int l = -1; l <= 1; l++) for (int r = -1; r <= 1; r++) { if (a - l < 0 || b - r < 0) continue; if (lr[i] && l == r && !(l == 0 && i == 1)) continue; if (l == -1 && !lu[i]) continue; if (l == 1 && !ld[i]) continue; if (r == -1 && !ru[i]) continue; if (r == 1 && !rd[i]) continue; dp[i][a][b] = max(dp[i][a][b], dp[i - 1][a - l][b - r] + (l == -1) + (r == -1)); } } for (int a = 0; a <= n; a++) for (int b = 0; b <= n; b++) ans = max(ans, dp[n][a][b]); printf("%d\n", cnt - ans); return0; }
voidinsert(longlong x){ for (int i = 51; i >= 0; i--) if (x >> i & 1) { if (!b[i]) {b[i] = x; break;} else x ^= b[i]; } return ; } longlongquery(longlong x){ for (int i = 51; i >= 0; i--) if ((x ^ b[i]) > x) x ^= b[i]; return x; } longlonggetmin(longlong x){ for (int i = 51; i >= 0; i--) if ((x ^ b[i]) < x) x ^= b[i]; return x; } } lb;
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) { longlong x; scanf("%lld", &x); lb.insert(x); } longlong val = lb.query(0); int num = n; for (int i = 51; i >= 0; i--) if (lb.b[i]) num--; // for (int i = 0; i <= 51; i++) eprintf("%lld ", lb.b[i]); // eputs(""); longlong ans = val * num; for (int i = 51, flag = 0; i >= 0; i--) if (lb.b[i]) { if (!flag) ans += val; else { longlong tmp = lb.b[i]; lb.b[i] = 0; ans += val ^ lb.getmin(tmp); // eprintf("%d %lld\n", i, lb.getmin(tmp)); lb.b[i] = tmp; } flag = 1; } printf("%lld\n", ans); return0; }
TopCoder AlienDictionary
给定若干个由 ?,A,B 组成的串,多次询问 k,求由 A, B 组成的,长度位为 n 的,合法的,字典序第 k 小的串,其中合法是不存在一个子串与先前给定的串匹配,其中 ? 可以匹配 A 或 B。
暴力拆开 ? 建 AC 自动机,设 dpi,j 表示从 j 走 i 步走到最终状态的方案数,然后试填法求答案。
int nxtc, prec, n; string s, t; unsignedlonglong ps[52], pt[26];
intcheck(){ for (int i = 0; i < 26; i++) if (!pt[i]) return0; return1; } intgetpos(unsignedlonglong sum){ for (int i = 0; i < 26; i++) if (pt[i] == sum) return i; return-1; } intcalc(unsignedlonglong *ps, int shift){ vector<tuple<int, int, int>> vec; for (int l = 0, r; l < 26; l = r + 1) { r = l; if (!ps[l]) continue; unsignedlonglong sum = ps[l]; while (r < 26 && getpos(sum) == -1) sum += ps[++r]; if (r == 26) return0x3f3f3f3f; vec.emplace_back(l, r, getpos(sum) - shift); // eprintf("%d %d %d\n", l, r, getpos(sum) - shift); } // eprintf("%d\n", shift); int len = 0; for (int i = 0; i < (int)vec.size(); i++) { int x = get<2>(vec[i]), y = get<2>(vec[(i + 1) % vec.size()]); len += y - x + (y < x ? 26 : 0); } if (vec.size() > 1 && len != 26) return0x3f3f3f3f; int ans = 0x3f3f3f3f; for (auto &i : vec) if (get<2>(i) > get<2>(vec.back())) get<2>(i) -= 26; for (int i = -4; i <= 4; i++) { int res = 0; for (auto j : vec) { int l = get<0>(j), r = get<1>(j), p = get<2>(j) + i * 26; if (p <= l) res += prec * (r - p); elseif (p >= r) res += nxtc * (p - l); else res += nxtc * (p - l) + prec * (r - p); } ans = min(ans, res); } return ans; }
intmain(){ scanf("%d%d", &nxtc, &prec); cin >> s >> t; n = s.length(); for (int i = 0; i < n; i++) { unsigned rnd = gen(); ps[s[i] - 'a'] += rnd, pt[t[i] - 'a'] += rnd; } if (s == t) {puts("0"); return0;} if (check()) {puts("-1"); return0;} for (int i = 0; i < 26; i++) ps[26 + i] = ps[i]; int ans = 0x3f3f3f3f; for (int i = 0; i < 26; i++) ans = min(ans, calc(ps + i, i)); if (ans != 0x3f3f3f3f) printf("%d\n", ans); elseputs("-1"); return0; }
TopCoder MaximalTriangle
给一个正 n 边形,求有多少种三角剖分的方式,使得存在一个三角形面积严格大于其他三角形。
枚举最大的三角形,把多边形切成三个部分,要求每部分最大面积小于枚举的三角形的面积。设 gi,j 表示 i 个点,面积小于等于 j 的方案数,枚举不在多边形上的那条边(即切割的弦)所在的三角形转移即可。
structFastMod { typedefunsignedlonglong ULL; typedef__uint128_t LLL; ULL b, m; voidinit(ULL b){ this->b = b, m = ULL((LLL(1) << 64) / b); } ULL operator()(ULL a){ ULL q = (ULL)((LLL(m) * a) >> 64); ULL r = a - q * b; return r >= b ? r - b : r; } } M; inlinelonglongmul(int a, int b){ returnM(1ull * a * b); }
int n, mod, m; longlong g[505]; double S[505][505];
doublecalcS(int i, int j){ int x = n - i - j; if (x < i) swap(i, x); if (x < j) swap(j, x); if (j < i) swap(i, j); j += i; returnVect(cos(2 * pi / n * i) - 1, sin(2 * pi / n * i)) * Vect(cos(2 * pi / n * j) - 1, sin(2 * pi / n * j)) * 1e5; }
intmain(){ ios::sync_with_stdio(0); cin.tie(0); cin >> n >> mod; M.init(mod); for (int i = 1; i < n; i++) for (int j = i + 1; j < n; j++) S[i][j - i] = calcS(i, j - i); longlong ans = 0; for (int i = 1; i < n; i++) for (int j = i + 1; j < n; j++) { int x = i, y = j - i, z = n - x - y; if (x > y || y > z) continue; memset(g, 0, sizeof(g)); g[2] = 1; for (int i = 2; i <= z + 1; i++) { g[i] %= mod; for (int j = 2, to = i + 1; j <= i && to <= z + 1; j++, to++) { constint o = (j < i); if (S[i - 1][j - 1] < S[x][y] - eps) g[to] += mul(g[i], g[j]) << o; } } int c = 2 * n; if (x == y || y == z) c = n; if (x == y && y == z) c = n / 3; ans = (ans + g[x + 1] * g[y + 1] % mod * g[z + 1] % mod * c) % mod; } cout << ans % mod << '\n'; return0; } /* 444 998244353 860426962 */
int n, tx, ty, m, cnt[45][45][15], fac[1000005], inv[1000005], dp[45][15]; structnode {int x, y, d;} a[45];
intC(int n, int m){return n >= m ? (longlong)fac[n] * inv[m] % mod * inv[n - m] % mod : 0;} intcalc(int x, int y, int k){ if (x < 0 || y < 0) return0; if (!x && !y) return !k; if (!k) return0; if (!x) returnC(y - 1 , k - 1); if (!y) returnC(x - 1 , k - 1); int res = 0; for (int i = 1; i < k; i++) res = (res + (longlong)C(k, i) * C(x - 1, i - 1) % mod * C(y - 1, k - i - 1)) % mod; // eprintf("calc(%d, %d, %d) = %d\n", x, y, k, res); return res; }
intmain(){ fac[0] = fac[1] = 1; inv[0] = inv[1] = 1; for (int i = 2; i <= 1000000; i++) { fac[i] = (longlong)fac[i - 1] * i % mod; inv[i] = (longlong)(mod - mod / i) * inv[mod % i] % mod; } for (int i = 2; i <= 1000000; i++) inv[i] = (longlong)inv[i] * inv[i - 1] % mod; scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i].x); scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i].y); scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i].d); scanf("%d%d%d", &tx, &ty, &m); a[++n] = {0, 0, 0}; sort(a + 1, a + n + 1, [](const node &u, const node &v) { return u.x != v.x ? u.x < v.x : u.y < v.y; }); while (a[n].x > tx || a[n].y > ty) n--; if (a[n].x != tx || a[n].y != ty) a[++n] = {tx, ty, 0}; // eprintf("%d\n", n); for (int i = 1; i <= n; i++) for (int j = i + 1; j <= n; j++) for (int k = 0; k <= m; k++) { cnt[i][j][k] = calc(a[j].x - a[i].x - a[i].d, a[j].y - a[i].y, k); if (a[i].d) cnt[i][j][k] = (cnt[i][j][k] + calc(a[j].x - a[i].x, a[j].y - a[i].y - a[i].d, k)) % mod; for (int l = i + 1; l < j; l++) for (int t = 0; t <= k; t++) cnt[i][j][k] = (cnt[i][j][k] + mod - (longlong)cnt[i][l][t] * calc(a[j].x - a[l].x, a[j].y - a[l].y, k - t) % mod) % mod; } dp[1][0] = 1; for (int i = 2; i <= n; i++) for (int j = 1; j < i; j++) for (int k = 0; k <= m; k++) for (int t = 0; t <= k; t++) dp[i][k] = (dp[i][k] + (longlong)dp[j][t] * cnt[j][i][k - t]) % mod; // eprintf("%d\n", cnt[1][2][2]); int ans = 0; for (int i = 0; i <= m; i++) ans = (ans + dp[n][i]) % mod; printf("%d\n", ans); return0; }
TopCoder WallGameDiv1
两个人在一个网格上玩游戏,(i,j) 上有数字 ai,j。A 先把棋子放在第一行的某个位置,然后每次 B 选择任意对上下相邻的格子在中间放一堵墙,A 再移动棋子,棋子可以左右下移动,不能穿过墙,B 放墙要保证 A 能到达最后一行,到达最后一行时游戏立即结束,分数是棋子经过的网格的数字之和,重复经过多次计算,A 要时分数最小,B 要使分数最大,求最终分数。
显然 B 在需要的时候再放墙,因此每层墙都是一个区间或留下一个洞,设 fi,l,r,0/1 表示在第 i 层,墙堵上了 [l,r],棋子在 l 或 r 时的答案,直接 DP 即可。
int n, m, deg[100005], p[100005], q[100005], vis[100005], id[100005]; structedge {int u, v;} e[100005]; vector<int> g[100005]; bitset<705> b[100005];
intmain(){ freopen("kfour.in", "r", stdin); freopen("kfour.out", "w", stdout); ios::sync_with_stdio(0); cin >> n >> m; for (int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v, deg[e[i].u]++, deg[e[i].v]++; for (int i = 1; i <= n; i++) p[i] = i; sort(p + 1, p + n + 1, [](constint &x, constint &y) { return deg[x] < deg[y] || (deg[x] == deg[y] && x < y); }); for (int i = 1; i <= n; i++) q[p[i]] = i; for (int i = 1; i <= m; i++) if (q[e[i].u] < q[e[i].v]) g[e[i].u].push_back(e[i].v); else g[e[i].v].push_back(e[i].u); for (int i = 1; i <= n; i++) sort(g[i].begin(), g[i].end(), [](constint &x, constint &y) {return q[x] < q[y];}); longlong ans = 0; for (int i = 1; i <= n; i++) { int idx = 0; for (int j : g[i]) vis[j] = 1, id[j] = ++idx; for (int j : g[i]) for (int k : g[j]) if (vis[k]) b[j][id[k]] = 1; for (int j : g[i]) for (int k : g[j]) if (vis[k]) ans += (b[j] & b[k]).count(); for (int j : g[i]) vis[j] = 0, b[j].reset(); } cout << "0 0 0 " << ans << '\n'; return0; }
int n; vector<vector<int>> a; int k; map<pair<vector<vector<int>>, int>, longlong> mp;
longlongdfs(vector<vector<int>> a, int k){ // for (vector<int> i : a) { // for (int j : i) eprintf("%d", j); // eputs(""); // } // eprintf("%d\n", k); if (mp.count({a, k})) return mp[{a, k}]; int n = a.size(), m = a[0].size(); int sum = 0; for (vector<int> i : a) for (int j : i) sum += j; if (!k) return mp[{a, k}] = sum; if (!sum) return0; if (k % 2) { vector<vector<int>> b(n + 2); for (int i = 0; i < n + 2; i++) { b[i].resize(m + 2); for (int j = 0; j < m + 2; j++) { int s = 0; for (int k = 0; k < 5; k++) { int x = i + gx[k] - 1, y = j + gy[k] - 1; if (x < 0 || x >= n || y < 0 || y >= m) continue; s += a[x][y]; } b[i][j] = s % 2; } } return mp[{a, k}] = dfs(b, k - 1); } longlong res = 0; for (int i = 0; i < min(n, 2); i++) for (int j = 0; j < min(m, 2); j++) { vector<vector<int>> b((n - i + 1) / 2, vector<int>((m - j + 1) / 2)); for (int x = i; x < n; x += 2) for (int y = j; y < m; y += 2) b[x / 2][y / 2] = a[x][y]; res += dfs(b, k / 2); } return mp[{a, k}] = res; }
int n, cnt[55][3]; double f[55][55][55], g[55][55][55], p[55][55][55][3]; double ans;
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &cnt[i][0]); scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &cnt[i][1]); scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &cnt[i][2]); f[0][0][0] = 1; for (int i = 1; i <= n; i++) { memcpy(g, f, sizeof(g)); for (int x = 0; x <= i; x++) for (int y = 0; y <= i - x; y++) for (int z = 0; z <= i - x - y; z++) { if (x) f[x][y][z] += g[x - 1][y][z] * (x + y + z) * cnt[i][0] / 300. / (n - x - y - z + 1); if (y) f[x][y][z] += g[x][y - 1][z] * (x + y + z) * cnt[i][1] / 300. / (n - x - y - z + 1); if (z) f[x][y][z] += g[x][y][z - 1] * (x + y + z) * cnt[i][2] / 300. / (n - x - y - z + 1); } } // eprintf("%lf\n", f[2][0][0]); for (int x = 0; x <= n; x++) for (int y = 0; y <= n - x; y++) for (int z = 0; z <= n - x - y; z++) { if (x) p[x - 1][y][z][0] = f[x][y][z] * x / (x + y + z); if (y) p[x][y - 1][z][1] = f[x][y][z] * y / (x + y + z); if (z) p[x][y][z - 1][2] = f[x][y][z] * z / (x + y + z); } for (int x = 0; x < n; x++) for (int y = 0; y < n - x; y++) for (int z = 0; z < n - x - y; z++) { double res = 0; for (int i = 0; i < 3; i++) res = max(res, p[x][y][z][i] * 3 + p[x][y][z][(i + 1) % 3]); // eprintf("%d %d %d %lf\n", x, y, z, res); ans += res; } printf("%.12lf\n", ans); return0; } /* 2 100 100 2 100 100 2 100 100 */
voidNTT(poly &g, int flag)const{ int n = g.size(); vector<unsignedlonglong> f(g.begin(), g.end()); vector<int> swp(n); for (int i = 0; i < n; i++) { swp[i] = swp[i >> 1] >> 1 | ((i & 1) * (n >> 1)); if (i < swp[i]) std::swap(f[i], f[swp[i]]); } for (int mid = 1; mid < n; mid <<= 1) { int w1 = power(flag ? G : invG, (mod - 1) / mid / 2); vector<int> w(mid); w[0] = 1; for (int i = 1; i < mid; i++) w[i] = (longlong)w[i - 1] * w1 % mod; for (int i = 0; i < n; i += mid << 1) for (int j = 0; j < mid; j++) { int t = (longlong)w[j] * f[i + mid + j] % mod; f[i + mid + j] = f[i + j] - t + mod; f[i + j] += t; } if (mid == 1 << 10) for (int i = 0; i < n; i++) f[i] %= mod; } int inv = flag ? 1 : power(n, mod - 2); for (int i = 0; i < n; i++) g[i] = f[i] % mod * inv % mod; return ; } poly operator*(poly b) const { poly a(*this); int n = 1, len = (int)(a.size() + b.size()) - 1; while (n < len) n <<= 1; a.resize(n), b.resize(n); NTT(a, 1), NTT(b, 1); poly c(n); for (int i = 0; i < n; i++) c[i] = (longlong)a[i] * b[i] % mod; NTT(c, 0); c.resize(len); return c; } poly operator*=(const poly &b) {return *this = *this * b;} }; } usingnamespace Poly;
int n, x, fac[1000005], inv[1000005]; int H[500005], ans[500005]; poly f, g, cdep, csiz;
intC(int n, int m){return (longlong)fac[n] * inv[m] % mod * inv[n - m] % mod;} intcalc(int x, int p){ return (longlong)C(2 * x - p - 2, x - p - 1) * (p + 1) % mod * power(x, mod - 2) % mod; }
intmain(){ freopen("schrodingerstomb.in", "r", stdin); freopen("schrodingerstomb.out", "w", stdout); scanf("%d%d", &n, &x); fac[0] = fac[1] = 1; inv[0] = inv[1] = 1; for (int i = 2; i <= 2 * n; i++) { fac[i] = (longlong)fac[i - 1] * i % mod; inv[i] = (longlong)(mod - mod / i) * inv[mod % i] % mod; } for (int i = 2; i <= 2 * n; i++) inv[i] = (longlong)inv[i - 1] * inv[i] % mod; H[0] = 1; for (int i = 1; i <= n; i++) H[i] = (C(2 * i, i) - C(2 * i, i - 1) + mod) % mod; f.resize(x), g.resize(n - x + 1); for (int i = 0; i < x; i++) f[i] = (longlong)calc(x, i) * inv[i] % mod; for (int i = 0; i < n - x + 1; i++) g[i] = (longlong)calc(n - x + 1, i) * inv[i] % mod; cdep = f * g; cdep.push_back(0); for (int i = n; i >= 1; i--) cdep[i] = (longlong)cdep[i - 1] * fac[i - 1] % mod; for (int i = 0; i < x; i++) f[i] = H[i]; for (int i = 0; i < n - x + 1; i++) g[i] = H[i]; csiz = f * g; csiz.push_back(0); for (int i = n; i >= 1; i--) csiz[i] = (longlong)csiz[i - 1] * H[n - i] % mod; ans[1] = (longlong)H[x - 1] * H[n - x] % mod; for (int i = 2; i <= n; i++) ans[i] = ((longlong)ans[i - 1] + mod - csiz[n - i + 2] + cdep[i]) % mod; for (int i = 1; i <= n; i++) printf("%d\n", ans[i]); return0; }
4.7
没啥有意思题。
4.8
TopCoder FindPolygons
给你一棵树,找两个不交的连通块使得它们同构,求最大连通块大小。
枚举一条边断开,再枚举其中一个的根,然后设 fu,v 表示第一棵树里的 u 和第二棵树里的 v 为根的最大连通块打小,转移就是两个点的儿子二分图最大权匹配。
int L, ans = 1e9; vector<pair<int, int>> vec[5005];
intmain(){ scanf("%d", &L); if (L == 2) {puts("-1"); return0;} for (int i = 0; i <= L; i++) for (int j = i; j <= L; j++) { int v = i * i + j * j; int kk = 0; for (int k = max<int>(sqrt(v) - 5, 0); k * k <= v; k++) if (k * k == v) {kk = k; break;} if (kk && kk <= L) vec[kk].emplace_back(i, j); } // for (auto i : vec[4]) eprintf("%d %d\n", i.first, i.second); for (int i = 1; i <= L - 2; i++) for (auto p : vec[i]) for (int j = i; j < L - i; j++) for (auto q : vec[j]) { int k = L - i - j; int a = abs(p.first - q.first), b = abs(p.second - q.second); if (p.second * q.first != q.second * p.first && k * k == a * a + b * b) ans = min(ans, max({abs(k - i), abs(k - j), abs(i - j)})); a = abs(p.first - q.second), b = abs(p.second - q.first); if (p.second * q.second != q.first * p.first && k * k == a * a + b * b) ans = min(ans, max({abs(k - i), abs(k - j), abs(i - j)})); } if (ans < 1e9) {printf("%d\n", ans); return0;} if (L % 2 == 0) {printf("%d\n", L % 4 == 0 ? 0 : 1); return0;} puts("-1"); return0; }
4.12
C
神题!
等概率随机一个 n 个点有标号有根树,每条边权值在 [0,m]∩N 中等概率随机,然后在根上放一颗棋子,两个人轮流移动,要求一条边经过次数不能超过它的边权,无法移动者输,问先手必胜的概率。
首先显然可以把边权对 2 取模,然后设 p0 为边权为 0 的概率。
朴素做法是考虑设 fi 为所有 i 个点的树先手必败的概率之和,然后设 gi 为所有 i 个点的树,钦定根的编号为 1 时先手必败的概率之和。
intpower(int a, int b){ // eprintf("%d %d\n", a, b); int ans = 1; while (b) { if (b & 1) ans = (longlong)ans * a % mod; a = (longlong)a * a % mod; b >>= 1; } return ans % mod; } intC(int n, int m){return (longlong)fac[n] * inv[m] % mod * inv[n - m] % mod;}
intmain(){ freopen("win.in", "r", stdin); freopen("win.out", "w", stdout); scanf("%d%d%d", &n, &p, &mod); p = (longlong)(p + 1) / 2 * power(p + 1, mod - 2) % mod; p = (mod - p) % mod; fac[0] = fac[1] = 1; inv[0] = inv[1] = 1; for (int i = 2; i <= n; i++) { fac[i] = (longlong)fac[i - 1] * i % mod; inv[i] = (longlong)(mod - mod / i) * inv[mod % i] % mod; } inv[0] = inv[1] = 1; for (int i = 2; i <= n; i++) inv[i] = (longlong)inv[i - 1] * inv[i] % mod; for (int i = 1; i <= n; i++) { int c = i < n ? power(n, n - i - 1) : power(n, mod - 2); ans = (ans + (longlong)power((longlong)i * p % mod, i - 1) * inv[n] % mod * i % mod * C(n, i) % mod * c) % mod; } // eputs("?"); ans = (longlong)ans * fac[n] % mod; ans = (power(n, n - 1) - ans + mod) % mod; ans = (longlong)ans * power(power(n, n - 1), mod - 2) % mod; printf("%d\n", ans); return0; }
4.13
都。是。屎。
4.15
TopCoder MagicMolecule
给一张无向图,点有点权,找一个大小大于等于 32n 的团使得点权和最大。
怎么每次看到这个套路都不会做……
维护一个集合 S,初始时是全集,每次扣出两个没有边的点从集合中删掉,最后 S 一定是一个团。并且如果存在大于等于 32n 的团则 ∣S∣≥3n,因此扣出来的点对也只有 3n,暴力枚举每个点对选哪个或不选,时间复杂度为 O(n33n)。
int n; int val[3005], siz[3005], son[3005], mp[3005][3005], vis[3005]; vector<int> g[3005];
intgetLCA(int u, int v){ return !mp[u][v] ? mp[u][v] = mp[v][u] = val[u] ^ val[v] ^ query(u, {v}) : mp[u][v]; } voiddfs(int now){ siz[now] = 1, son[now] = 0; for (int v : g[now]) { dfs(v); siz[now] += siz[v]; if (siz[v] > siz[son[now]]) son[now] = v; } return ; } voiderase(int u, int v){ for (int i = 0; i < (int)g[u].size(); i++) if (g[u][i] == v) g[u].erase(g[u].begin() + i); return ; } intQuery(int u, vector<int> &S){ int ans = 0; for (int i : S) ans ^= val[u] ^ val[i]; ans ^= query(u, S); return ans; } voidinsert(int u, int now){ // assert(vis[now]); // eprintf("insert(%d, %d);\n", u, now); if (g[now].empty()) {g[now].push_back(u); return ;} vector<int> vec; vec.push_back(now); while (son[vec.back()]) vec.push_back(son[vec.back()]); int tmp = getLCA(vec.back(), u); if (tmp != now) { for (int i : vec) if (tmp == i) {insert(u, tmp); return ;} int l = 0, r = vec.size() - 1; while (l < r) { int mid = (l + r) / 2 + 1; if (getLCA(vec[mid], u) == vec[mid]) l = mid; else r = mid - 1; } erase(vec[l], vec[l + 1]); g[vec[l]].push_back(tmp); g[tmp].push_back(vec[l + 1]); if (tmp != u) g[tmp].push_back(u); vis[tmp] = 1; return ; } vector<int>().swap(vec); for (int v : g[now]) if (v != son[now]) vec.push_back(v); if (vec.size() % 2) vec.push_back(son[now]); // eprintf("%d\n", (int)vec.size()); if (vec.empty()) {g[now].push_back(u); return ;} tmp = Query(u, vec); if (!tmp) {g[now].push_back(u); return ;} if (!vis[tmp ^ now]) { int l = 0, r = vec.size() - 1; while (l < r) { int mid = (l + r) / 2; vector<int> qvec(vec.begin(), vec.begin() + mid + 1); int tmp = Query(u, qvec); if (tmp != 0 && tmp != now) r = mid; else l = mid + 1; } erase(now, vec[r]); tmp ^= now; g[now].push_back(tmp); g[tmp].push_back(vec[r]); if (tmp != u) g[tmp].push_back(u); vis[tmp] = 1; return ; } insert(u, tmp ^ now); return ; }
voidsolve(int n, int Q, int dataType){ ::n = n; val[1] = 1; for (int i = 2; i <= n; i++) val[i] = query(1, {i}); for (int i = 1; i <= n; i++) vector<int>().swap(g[i]); memset(mp, 0, sizeof(mp)); memset(siz, 0, sizeof(siz)); memset(son, 0, sizeof(son)); memset(vis, 0, sizeof(vis)); vis[1] = 1; for (int i = 2; i <= n; i++) if (!vis[i]) insert(i, 1), vis[i] = 1, dfs(1); for (int i = 1; i <= n; i++) for (int j : g[i]) report(i, j); return ; }
4.20
TopCoder HungryCowsMedium
数轴原点有 n 头牛,还有 m 个谷仓,每一时刻每头牛可以往左移动一单位距离或往右移动一单位距离或原地不懂,当与某一谷仓在同一位置时也可以吃一单位食物,同一时刻一个谷仓至多有一头牛在吃食物。第 i 头牛要吃 ai 单位食物,问需要的最小时间。
胡乱分析。
显然可以二分答案,设答案为 T,则第 i 头牛最多移动 T−ai 单位距离,因此其只能吃到位置在 [0,T−ai] 的谷仓。
intcheck(longlong limit){ longlong t = 0; int j = 1; for (int i = 1; i <= m && j <= n; i++) { if (barn[i] + cow[j] > limit) return0; t += limit - barn[i] - cow[j]; j++; while (j <= n && cow[j] <= t) t -= cow[j++]; } return j > n; }
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &cow[i]); sort(cow + 1, cow + n + 1, greater<>()); scanf("%d", &m); for (int i = 1; i <= m; i++) scanf("%d", &barn[i]); sort(barn + 1, barn + m + 1); longlong l = 0, r = 4e11; while (l < r) { longlong mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid + 1; } printf("%lld\n", r); return0; }
TopCoder FoxConnection
有一棵 n 个点的树,其中某些点上有狐狸,每次可以让一只狐狸移动到相邻的节点,要求最终每个点至多有一只狐狸且有狐狸的点形成一个连通块,求最小移动距离之和。
设 dpi,j 表示 i 子树中有 j 只狐狸,且 i 上有狐狸的答案,枚举儿子,然后枚举儿子子树内的狐狸数,算出这条边被经过的次数然后转移即可。
int n, m, a[55], siz[55]; int ans = 0x3f3f3f3f, dp[55][55], tmp[55]; structedge {int x, y;} e[55]; structgraph { int tot, hd[55]; int nxt[105], to[105];
voidadd(int u, int v){ nxt[++tot] = hd[u]; hd[u] = tot; to[tot] = v; return ; } } g;
voiddfs(int now, int fa){ siz[now] = a[now]; memset(dp[now], 0, sizeof(dp[now])); for (int i = g.hd[now]; i; i = g.nxt[i]) if (g.to[i] != fa) { dfs(g.to[i], now); memset(tmp, 0x3f, sizeof(tmp)); for (int j = 0; j <= m; j++) for (int k = 0; k <= m - j; k++) tmp[j + k] = min(tmp[j + k], dp[now][j] + dp[g.to[i]][k] + abs(siz[g.to[i]] - k)); memcpy(dp[now], tmp, sizeof(dp[now])); siz[now] += siz[g.to[i]]; } for (int i = m; i >= 1; i--) dp[now][i] = dp[now][i - 1]; return ; }
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &e[i].x); scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &e[i].y); n++; for (int i = 1; i < n; i++) g.add(e[i].x, e[i].y), g.add(e[i].y, e[i].x); for (int i = 1; i <= n; i++) { char c; cin >> c; m += (a[i] = c == 'Y'); } for (int i = 1; i <= n; i++) { dfs(i, 0); // eprintf("%d %d\n", i, dp[3][1]); // for (int j = 1; j <= n; j++) eprintf("%d ", dep[j]); // eputs(""); ans = min(ans, dp[i][m]); } printf("%d\n", ans); return0; }
TopCoder FoxSearchingRuins
W×H 的网格上有 n 个宝石,第 i 个宝石在第 xi 列 yi 行,价值 vi,初始时从第 0 行任意列出发,给出左右移动的次数限制和左右移动一单位需要的时间,向下移动一单位需要的时间,问获得的宝石的价值之和达到目标需要的最小的时间。
设 dpi,j 表示在宝石 i 的位置,左右移动了 j 次的最大价值,显然时间就是 j 乘左右移动时间加 i 的深度乘向下移动的时间。
for (int i = 1; i <= n; i++) ry[i] = a[i].y; sort(ry + 1, ry + n + 1); _ = unique(ry + 1, ry + n + 1) - ry - 1; for (int i = 1; i <= n; i++) { a[i].y = lower_bound(ry + 1, ry + _ + 1, a[i].y) - ry; if (mp[{a[i].x, ry[a[i].y]}] == i) vec[a[i].y].push_back(i); } // for (int i = 0; i < h; i++, eputs("")) // for (int j = 0; j < w; j++) eprintf("%d ", MAP[j][i]);
for (int i = 1; i <= _; i++) sort(vec[i].begin(), vec[i].end(), [](constint &x, constint &y) { return a[x].x < a[y].x; });
for (int i = 1; i <= _; i++) { for (int j = 0; j <= m; j++) for (int k = 0; k < (int)vec[i].size(); k++) for (int o = 0; o < 3; o++) { int now = vec[i][k]; if (!o){ dp[now][j][0] = max(dp[now][j][0], mx[0][j + a[now].x + 1000].query(w - a[now].x)); dp[now][j][0] = max(dp[now][j][0], mx[1][j - a[now].x + 1000].query(a[now].x + 1)); }
if (dp[now][j][o] >= goal) { ans = min(ans, (longlong)j * lrcost + (longlong)ry[i] * dcost); // if (ans < 0x3f3f3f3f3f3f3f3f) eprintf("%d %d %d %d\n", now, j, o, dp[now][j][o]); }
if (!o) { dp[now][j][1] = max(dp[now][j][1], dp[now][j][0] + a[now].v); dp[now][j][2] = max(dp[now][j][2], dp[now][j][0] + a[now].v); } else { if (o == 1 && k > 0) { int to = vec[i][k - 1]; if (j + a[now].x - a[to].x <= m) dp[to][j + a[now].x - a[to].x][1] = max(dp[to][j + a[now].x - a[to].x][1], dp[now][j][1] + a[to].v); } if (o == 2 && k < (int)vec[i].size() - 1) { int to = vec[i][k + 1]; if (j + a[to].x - a[now].x <= m) dp[to][j + a[to].x - a[now].x][2] = max(dp[to][j + a[to].x - a[now].x][2], dp[now][j][2] + a[to].v); } } }
for (int j = 0; j <= m; j++) for (int k = 0; k < (int)vec[i].size(); k++) for (int o = 1; o < 3; o++) { int now = vec[i][k]; mx[0][j + a[now].x + 1000].update(w - a[now].x, dp[now][j][o]); mx[1][j - a[now].x + 1000].update(a[now].x + 1, dp[now][j][o]); } } // eprintf("%d\n", dp[2][3][2]); if (ans == 0x3f3f3f3f3f3f3f3f) puts("-1"); elseprintf("%lld\n", ans); return0; }
int n, m, nxt[505]; string s, t[55]; longlong k; double dp[505]; pair<double, double> operator*(const pair<double, double> &x, const pair<double, double> &y) { returnmake_pair(x.first * y.second + x.second * y.first, x.second * y.second); } pair<double, double> operator+(const pair<double, double> &x, const pair<double, double> &y) { returnmake_pair(x.first + y.first, x.second + y.second); } structMatrix { int n, m; pair<double, double> mat[505][505];
Matrix(int _n = 0, int _m = 0): n(_n), m(_m) {memset(mat, 0, sizeof(mat));} Matrix operator*(const Matrix &a) { Matrix ans(n, a.m); for (int i = 1; i <= n; i++) for (int k = 1; k <= m; k++) for (int j = 1; j <= a.m; j++) ans.mat[i][j] = ans.mat[i][j] + mat[i][k] * a.mat[k][j]; return ans; } } f, g;
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) cin >> t[i]; scanf("%d", &m); while (m--) { string ss; cin >> ss; s += ss; } m = s.length(); s = " " + s; for (int i = 2, j = 0; i <= m; i++) { while (j && s[j + 1] != s[i]) j = nxt[j]; if (s[j + 1] == s[i]) nxt[i] = ++j; } f.n = 1, f.m = m; f.mat[1][m].second = 1; g.n = g.m = m; for (int i = 1; i <= m; i++) for (int j = 1; j <= n; j++) { int now = i % m, cnt = 0; for (char k : t[j]) { while (now && s[now + 1] != k) now = nxt[now]; if (s[now + 1] == k) if (++now == m) now = nxt[now], cnt++; } if (!now) now = m; // eprintf("%d %d %d\n", i, now, cnt); g.mat[i][now] = g.mat[i][now] + make_pair((double)cnt / n, 1. / n); } scanf("%lld", &k); for (int i = 1; i <= m + 1; i++) { f = f * g; for (int j = 1; j <= m; j++) dp[i] += f.mat[1][j].first; } if (k <= m) {printf("%.12lf\n", dp[k]); return0;} // eprintf("%lf %lf\n", dp[2], dp[3]); // eprintf("%.12lf\n", dp[m]); double ans = dp[m] + (dp[m + 1] - dp[m]) * (k - m); printf("%.12lf\n", ans); return0; }
TopCoder RabbitPuzzle
数轴上有三只兔子,坐标为 (A,B,C),每次可以选两只兔子,将第一只兔子以第二只兔子为对称中心对称,要求不能跳过第三只兔子,问跳 k 次到达目标坐标的方案数。
longlong sx, sy, sz, ex, ey, ez; int n, dp[105][105][105]; vector<tuple<longlong, longlong, longlong>> anc[2];
tuple<longlong, longlong, longlong> getfa(tuple<longlong, longlong, longlong> u){ longlong A = get<0>(u), B = get<1>(u), C = get<2>(u); if (B - A == C - B) return {0, 0, 0}; elseif (B - A < C - B) return {B, 2 * B - A, C}; elsereturn {A, 2 * B - C, B}; }
intmain(){ scanf("%d%lld%lld%lld", &n, &sx, &sy, &sz); scanf("%d%lld%lld%lld", &n, &ex, &ey, &ez); scanf("%d", &n); anc[0].emplace_back(sx, sy, sz); for (int i = 1; i <= n; i++) { tuple<longlong, longlong, longlong>fa = getfa(anc[0].back()); if (get<0>(fa) == get<1>(fa)) break; anc[0].push_back(fa); } anc[1].emplace_back(ex, ey, ez); for (int i = 1; i <= n; i++) { tuple<longlong, longlong, longlong>fa = getfa(anc[1].back()); if (get<0>(fa) == get<1>(fa)) break; anc[1].push_back(fa); } // for (auto i : anc[0]) eprintf("%lld %lld %lld\n", get<0>(i), get<1>(i), get<2>(i)); // eputs(""); // for (auto i : anc[1]) eprintf("%lld %lld %lld\n", get<0>(i), get<1>(i), get<2>(i)); int sdep = -1, tdep = -1; for (int i = 0; i < (int)anc[0].size(); i++) { for (int j = 0; j < (int)anc[1].size(); j++) if (anc[0][i] == anc[1][j]) {sdep = i; tdep = j; break;} if (sdep != -1) break; } // eprintf("%d %d\n", sdep, tdep); if (sdep == -1) {puts("0"); return0;} // eputs("?"); dp[0][sdep][tdep] = 1; for (int i = 1; i <= n; i++) for (int j = 0; j <= n; j++) for (int k = 0; k <= n; k++) { if (j) dp[i][j + 1][k] = (dp[i][j + 1][k] + dp[i - 1][j][k] * 2ll) % mod; else { dp[i][j + 1][k] = (dp[i][j + 1][k] + dp[i - 1][j][k]) % mod; if (k) dp[i][j][k - 1] = (dp[i][j][k - 1] + dp[i - 1][j][k]) % mod; else dp[i][j + 1][k] = (dp[i][j + 1][k] + dp[i - 1][j][k]) % mod; } if (j) dp[i][j - 1][k] = (dp[i][j - 1][k] + dp[i - 1][j][k]) % mod; elseif (k + 1 < (int)anc[1].size()) dp[i][j][k + 1] = (dp[i][j][k + 1] + dp[i - 1][j][k]) % mod; } printf("%d\n", dp[n][0][0]); return0; }
4.23
训练赛盲盒终于开出一场质量较高的了。
TopCoder CycleColoring
有 n 个环,第 i 个环的长度为 Li,每个环上的边是实边,另有 n 条虚边,第 i 条连接第 i 个环上第 ui 个点和第 (i+1)modn 个环上第 vi 个点。要给每个点染上 m 种颜色中的一种,使得实边连接的两个点颜色不同,虚边连接的两个点颜色相同。
常规想法是钦定一个点的颜色然后往后递推,显然最好的方法是钦定第一个环上与最后一个环相连的点,然后一个环一个环推过去,这样每个环都会先钦定一个点其他任意,我们记 dpi,0/1 表示考虑到第 i 个环,与第 i−1 个环想连的点的颜色是 / 不是与最开始那个点的颜色相同,转移就考虑与第 i+1 个环相连的那个点的颜色,只需要求出长度为 l 的链,钦定两端颜色,且这两端颜色相同 / 不相同时的方案数即可。
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &p[i]); scanf("%d", &limit); for (int i = 1; i <= n; i++) if (p[n] == -1 || p[n] == i) if (i <= limit - 74) dp[1 << (i - 1)][i] = 1; for (int S = 1; S < 1 << n; S++) for (int mn = 1; mn <= min(limit - 74, 2 * n - 1); mn++) { int pos = n - __builtin_popcount(S); for (int i = n, flag = 1; i >= 1; i--) if (!(S >> (i - 1) & 1)) { // eprintf("%d %d\n", S, i); if (p[pos] == -1 || p[pos] == i) { int cost = n + i - pos + 74; if (n + i - pos > mn) cost += 74; if (cost <= limit && (n + i - pos <= mn || flag)) dp[S | 1 << (i - 1)][min(n + i - pos, mn)] += dp[S][mn]; } flag = 0; } } // eprintf("%lld\n", dp[3][2]); longlong ans = 0; for (int i = 1; i <= min(limit - 74, 2 * n - 1); i++) ans += dp[(1 << n) - 1][i]; printf("%lld\n", ans); return0; }
TopCoder TheCitiesAndRoadsDivOne
给你一张图,你要补上一些边使得图变成一棵基环树或树,求方案数。
枚举环上的点,然后环的方案数是好求的,接下来就是把环缩成一个连通块,然后变成 n 个点有若干个连通块连成树的方案树,这个是经典结论,设有 m 个连通块,每个连通块大小为 di,则答案为:
int n, g[25], m, ans; structedge {int u, v;} e[405]; structdsu { int fa[25], cnt[25];
voidclear(){for (int i = 0; i < 25; i++) fa[i] = i, cnt[i] = 1; return ;} intfind(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);} voidmerge(int x, int y){ int fx = find(x), fy = find(y); if (fx == fy) return ; cnt[fy] += cnt[fx]; fa[find(x)] = find(y); return ; } intquery(int x, int y){returnfind(x) == find(y);} } d;
intmain(){ scanf("%d", &n); for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { char c; cin >> c; if (c == 'Y') g[i] |= 1 << j, g[j] |= 1 << i; if (c == 'Y' && i < j) e[++m] = {i, j}; } if (m > n) {puts("0"); return0;} { int flag = 1; d.clear(); for (int i = 1; i <= m; i++) if (d.query(e[i].u, e[i].v)) flag = 0; else d.merge(e[i].u, e[i].v); ans = 1; int cnt = 0; for (int i = 0; i < n; i++) if (d.find(i) == i) ans = (longlong)ans * d.cnt[i] % mod, cnt++; for (int i = 1; i <= cnt - 2; i++) ans = (longlong)ans * n % mod; if (cnt == 1) ans = 1; ans *= flag; // eprintf("%d\n", ans); } for (int S = 1; S < 1 << n; S++) if (__builtin_popcount(S) > 2) { int flag = 1, c = 0; d.clear(); for (int i = 0; i < n; i++) if (S >> i & 1) { if (__builtin_popcount(g[i] & S) > 2) flag = 0; for (int T = g[i] & S; T; T -= T & -T) { int j = __builtin_ctz(T); if (i > j) continue; if (d.query(i, j)) c = 1; d.merge(i, j); } } // eprintf("%d %d\n", S, flag); if (!flag) continue; int res = 1, cnt = 0; for (int i = 0; i < n; i++) if (S >> i & 1 && d.find(i) == i) res = (longlong)res * (1 + (d.cnt[i] > 1)) % mod, cnt++; if (cnt > 1 && c) continue; for (int i = 1; i < cnt; i++) res = (longlong)res * i % mod; res = (longlong)res * inv2 % mod; for (int i = 0; i < n; i++) if (S >> i & 1) d.merge(i, __builtin_ctz(S)); for (int i = 1; i <= m; i++) if (!(S >> e[i].u & 1) || !(S >> e[i].v & 1)) { // eputs("?"); // if (S == 7) eprintf("%d %d\n", e[i].u, e[i].v); if (d.query(e[i].u, e[i].v)) flag = 0; else d.merge(e[i].u, e[i].v); } if (!flag) continue; cnt = 0; int tmp = 1; for (int i = 0; i < n; i++) if (d.find(i) == i) tmp = (longlong)tmp * d.cnt[i] % mod, cnt++; for (int i = 1; i <= cnt - 2; i++) tmp = (longlong)tmp * n % mod; // eprintf("%d %d\n", S, d.find(0)); if (cnt > 1) res = (longlong)res * tmp % mod; ans = ((longlong)ans + res) % mod; } printf("%d\n", ans); return0; }
int n, a[2505]; int n2, n3, n5, n7; int two[8405], three[5305], dp[8405][5305]; string s;
intcheck(int x){ int mod = 0; for (int i = n - 1; i >= 0; i--) { mod = mod * 10 + a[i]; mod %= x; } return !mod; } voiddiv(int x){ int mod = 0; for (int i = n - 1; i >= 0; i--) { mod = mod * 10 + a[i]; a[i] = mod / x; mod %= x; } while (!a[n - 1]) n--; return ; }
intmain(){ scanf("%d", &n); while (n--) { string ss; cin >> ss; s += ss; } n = s.length(); for (int i = 0; i < n; i++) a[i] = s[n - i - 1] - '0'; // for (int i = n - 1; i >= 0; i--) eprintf("%d", a[i]); // eputs(""); while (check(2)) div(2), n2++; while (check(3)) div(3), n3++; while (check(5)) div(5), n5++; while (check(7)) div(7), n7++; while (check(11)) div(11); while (check(13)) div(13); if (n > 1 || a[0] != 1) {puts("0"); return0;} // eprintf("%d %d %d %d\n", n2, n3, n5, n7); two[0] = 1; for (int i = 1; i <= 4; i++) for (int j = i; j <= n2; j++) two[j] = (two[j] + two[j - i]) % mod; three[0] = 1; for (int i = 1; i <= 2; i++) for (int j = i; j <= n3; j++) three[j] = (three[j] + three[j - i]) % mod; for (int i = 0; i <= n2; i++) for (int j = 0; j <= n3; j++) dp[i][j] = (longlong)two[i] * three[j] % mod; for (int i = 1; i <= n2; i++) for (int j = 1; j <= n3; j++) dp[i][j] = (dp[i][j] + dp[i - 1][j - 1]) % mod; for (int i = 2; i <= n2; i++) for (int j = 1; j <= n3; j++) dp[i][j] = (dp[i][j] + dp[i - 2][j - 1]) % mod; int ans = 0; for (int i = 0; i <= n2; i++) for (int j = 0; j <= n3; j++) { if (j > n5 || i > n5 - j + n7) continue; // eprintf("%d %d\n", i, j); ans = (ans + (longlong)dp[n2 - i][n3 - j] * (min(n5 - j, i) - max(0, i - n7) + 1)) % mod; } // x, i - x : 0 <= x <= n5 - j, i >= x >= i - n7 printf("%d\n", ans); return0; }
4.26
A
有一个 7 位字符串,字符集为 5,给定 n 条限制,每条限制限定每一位大于或小于或等于某个字符,所有限制可以推出矛盾。以任意顺序给出这些限制,在这些限制出现矛盾时停止,问在 1∼n 时刻停止的本质不同的方案数。
把限制看作超立方体,矛盾就是超立方体无交。
显然出现矛盾的一定是两条限制,考虑在不矛盾的限制之间连边,设 fi 为大小为 i 的团的数量,则 i 的答案就是 fi−1×(i−1)!×(n−i+1)−fi×i!。
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++, puts("")) for (int S = 0; S < 1 << (n - 1); S++) { int res = i; for (int T = S; T; T >>= 1) if (!(T & 1)) res ^=__builtin_popcount(T); putchar('B' + (res & 1)); } return0; }
4.29
A
有 n 个灯,前 v 个是开着的,每次可以选择若干个位置将状态反转,问选 m 种本质不同的操作使得最终灯全关掉的方案数。
int T, n; longlong a[1000005]; vector<pair<longlong, longlong>> ans;
intcheck(){ vector<longlong> vec[2]; for (int i = 1; i <= n; i++) vec[a[i] & 1].push_back(a[i]); shuffle(vec[0].begin(), vec[0].end(), gen); shuffle(vec[1].begin(), vec[1].end(), gen); vector<pair<longlong, longlong>> res; while (vec[0].size() > 1 || vec[1].size() > 1) { int o = gen() & 1; if (vec[o].size() < 2) o ^= 1; longlong x = vec[o].back(); vec[o].pop_back(); longlong y = vec[o].back(); vec[o].pop_back(); res.emplace_back(x, y); int oo = (x + y) / 2 & 1; vec[oo].push_back((x + y) / 2); } if (vec[0].size() + vec[1].size() == 1) {ans.swap(res); return1;} return0; }
intmain(){ freopen("cow.in", "r", stdin); freopen("cow.out", "w", stdout); scanf("%d", &T); while (T--) { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%lld", &a[i]); int flag = 0; for (int i = 1; i <= 400; i++) if (check()) {flag = 1; break;} if (!flag) {puts("-1"); continue;} for (auto i : ans) printf("%lld %lld\n", i.first, i.second); } return0; }
int n, m, a[105][105], dp[105][105][105][4], sum[105][105][105][4], ans;
intcalc(int i, int l1, int l2, int r1, int r2, int s){ return ((longlong)sum[i][l2][r2][s] + mod - sum[i][l1 - 1][r2][s] + mod - sum[i][l2][r1 - 1][s] + sum[i][l1 - 1][r1 - 1][s]) % mod; }
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) { string s; cin >> s; m = s.length() * 2; s = " " + s; for (int j = 1; j <= m / 2; j++) { int v; if (isdigit(s[j])) v = s[j] - '0'; else v = s[j] - 'a' + 10; a[2 * i - 1][2 * j - 1] = v & 1; a[2 * i - 1][2 * j] = v >> 1 & 1; a[2 * i][2 * j - 1] = v >> 2 & 1; a[2 * i][2 * j] = v >> 3 & 1; } } n *= 2; // for (int i = 1; i <= n; i++, eputs("")) // for (int j = 1; j <= m; j++) eprintf("%d", a[i][j]); for (int i = 1; i <= n; i++) { for (int l = 1; l <= m; l++) for (int r = l; r <= m; r++) if (!a[i][r]) { dp[i][l][r][0] = 1; for (int s = 0; s < 4; s++) for (int t = s; ; t = (t - 1) & s) { int ll = l - ((s ^ t) & 1), rr = r + ((s ^ t) >> 1 & 1); int l1, l2, r1, r2; if (s & 1) l1 = 1, l2 = ll; else l1 = ll, l2 = r; if (s >> 1 & 1) r1 = rr, r2 = m; else r1 = l, r2 = rr; dp[i][l][r][s] = (dp[i][l][r][s] + calc(i - 1, l1, l2, r1, r2, t)) % mod; if (!t) break; } } elsebreak; for (int l = 1; l <= m; l++) for (int r = 1; r <= m; r++) for (int s = 0; s < 4; s++) { ans = (ans + dp[i][l][r][s]) % mod; sum[i][l][r][s] = ((longlong)sum[i][l - 1][r][s] + sum[i][l][r - 1][s] + mod - sum[i][l - 1][r - 1][s] + dp[i][l][r][s]) % mod; } } // eprintf("%d\n", dp[2][1][1][0]); printf("%d\n", ans); return0; }
intf(int n, int m){ if (!n) return m % 2; if (!m) return n % 2; if (n == 1) return3 - m % 2; if (m == 1) return3 - n % 2; return1 - (n + m) % 2; }
intmain(){ scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { int l = 0, r = 0, mid = 0; for (int j = 1; j <= m; j++) { int x; scanf("%1d", &x); if (j <= (m + 1) / 2) l += x; if (j > m / 2) r += x; if (m % 2 && j == m / 2 + 1) mid += x; } l -= mid, r -= mid; if (!mid) ans ^= (l + r) % 2; else ans ^= f(l, r); } puts(ans ? "Alice" : "Bob"); return0; } /* f[n][m] = mex(f[n - 1][m], f[n][m - 1], (n + m) % 2) f[n][0] = f[0][n] = n % 2 4 3 111 101 110 010 2 ^ 0 ^ 1 ^ 0 */
J
给一个自动机,求一个串使得泵长度小于状态集大小,要求串长不超过状态集大小的 3 倍。
也就是找一条起始状态到任意一个结束状态的路径使得他有环。
先缩点,然后记一下每个强连通分量有没有环,找一条合法的路径,接下来就是构造强连通分量内部的路径。
现在就是要在一个强连通分量中从 u 走到 v 并走一个环。
我的构造就是先从 u 走到根,这个可以通过走树边和反祖 / 横叉边实现,需要注意跳到的点的 low 必须是经过的所有点的 low 的最小值,否则会在一个环上死循环。
然后在根上走一个环,显然直接找一个只有树边和一条反祖边的环就行。
最后从根走树边到 v 就行了。
int n, m, k, f[500005], intr[1000005]; structdfa { int tot, hd[500005]; int nxt[1000005], to[1000005], dt[1000005];
voidadd(int u, int v, int c){ nxt[++tot] = hd[u]; hd[u] = tot; to[tot] = v; dt[tot] = c; return ; } } dfa; structgraph { int tot, hd[500005]; int nxt[1000005], to[1000005], ru[1000005], rc[1000005];
voidadd(int u, int v, int _ru, int _rc){ nxt[++tot] = hd[u]; hd[u] = tot; to[tot] = v; ru[tot] = _ru, rc[tot] = _rc; return ; } } g; int timer, dfn[500005], low[500005], scc_cnt, scc[500005], rt[500005], cnt[500005]; vector<int> stk; int in[500005], pre[500005][2]; string ans;
voidTarjan(int now){ dfn[now] = low[now] = ++timer; stk.push_back(now); for (int i = dfa.hd[now]; i; i = dfa.nxt[i]) if (!dfn[dfa.to[i]]) Tarjan(dfa.to[i]), intr[i] = 1, low[now] = min(low[now], low[dfa.to[i]]); elseif (!scc[dfa.to[i]]) low[now] = min(low[now], dfn[dfa.to[i]]); if (low[now] == dfn[now]) { rt[++scc_cnt] = now; for (int x = 0; x != now; stk.pop_back()) { x = stk.back(); scc[x] = scc_cnt; cnt[scc_cnt]++; } for (int i = dfa.hd[now]; i; i = dfa.nxt[i]) if (dfa.to[i] == now) {cnt[scc_cnt]++; break;} } return ; } inttoroot(int now, int l, string &ans){ // eprintf("%d %d\n", now, l); if (now == rt[scc[now]]) return1; for (int i = dfa.hd[now]; i; i = dfa.nxt[i]) if (dfn[dfa.to[i]] > dfn[now]) { if (intr[i] && scc[dfa.to[i]] == scc[now]) if (toroot(dfa.to[i], l, ans)) {ans.push_back(dfa.dt[i] + 'a'); return1;} } elseif (scc[dfa.to[i]] == scc[now] && dfn[dfa.to[i]] < l) { if (toroot(dfa.to[i], dfn[dfa.to[i]], ans)) {ans.push_back(dfa.dt[i] + 'a'); return1;} } return0; } intcycle(int now, string &ans){ for (int i = dfa.hd[now]; i; i = dfa.nxt[i]) if (dfa.to[i] == rt[scc[now]]) {ans.push_back(dfa.dt[i] + 'a'); return1;} elseif (intr[i] && scc[dfa.to[i]] == scc[now]) if (cycle(dfa.to[i], ans)) {ans.push_back(dfa.dt[i] + 'a'); return1;} return0; } inttopoint(int now, int to, string &ans){ if (now == to) return1; for (int i = dfa.hd[now]; i; i = dfa.nxt[i]) if (intr[i] && scc[dfa.to[i]] == scc[now]) if (topoint(dfa.to[i], to, ans)) {ans.push_back(dfa.dt[i] + 'a'); return1;} return0; } voidgetans(int u, int v){ // eprintf("%d %d\n", u, v); string tmp; toroot(u, dfn[u], tmp); reverse(tmp.begin(), tmp.end()); ans += tmp; if (cnt[scc[u]] > 1) { string().swap(tmp); cycle(rt[scc[u]], tmp); reverse(tmp.begin(), tmp.end()); ans += tmp; // cerr << tmp << '\n'; } string().swap(tmp); // if (u == 1 && v == 2) eputs("?"); topoint(rt[scc[u]], v, tmp); reverse(tmp.begin(), tmp.end()); ans += tmp; return ; }
intmain(){ // freopen("in.txt", "r", stdin); ios::sync_with_stdio(0); cin.tie(0); cin >> n >> m >> k; for (int i = 1; i <= k; i++) { int x; cin >> x; f[x] = 1; } for (int i = 1; i <= m; i++) { int u, v; char c; cin >> u >> v >> c; dfa.add(u, v, c - 'a'); } for (int i = 1; i <= n; i++) if (!dfn[i]) Tarjan(i); // for (int i = 1; i <= n; i++) eprintf("%d ", rt[i]); for (int i = 1; i <= n; i++) for (int j = dfa.hd[i]; j; j = dfa.nxt[j]) if (scc[dfa.to[j]] != scc[i]) g.add(scc[i], scc[dfa.to[j]], i, j), in[scc[dfa.to[j]]]++; queue<int> q; for (int i = 1; i <= n; i++) if (!in[i]) q.push(i); while (!q.empty()) { int now = q.front(); q.pop(); if (scc[1] == now) pre[now][0] = -1, pre[now][1] = cnt[now] > 1 ? -1 : 0; for (int i = g.hd[now]; i; i = g.nxt[i]) { if (pre[now][0]) { pre[g.to[i]][0] = now; if (cnt[g.to[i]] > 1) pre[g.to[i]][1] = now; } if (pre[now][1]) pre[g.to[i]][1] = now; if (!--in[g.to[i]]) q.push(g.to[i]); } } int t = 0; for (int i = 1; i <= n; i++) if (f[i] && pre[scc[i]][1]) {t = i; break;} if (!t) {puts("-1"); return0;} vector<int> vec; for (int i = scc[t]; i != -1; ) { vec.push_back(i); if (pre[i][1]) i = pre[i][1]; else i = pre[i][0]; } reverse(vec.begin(), vec.end()); int now = 1; for (int i = 0; i < (int)vec.size() - 1; i++) { for (int j = g.hd[vec[i]]; j; j = g.nxt[j]) if (g.to[j] == vec[i + 1]) { getans(now, g.ru[j]); ans.push_back(dfa.dt[g.rc[j]] + 'a'); now = dfa.to[g.rc[j]]; break; } } // eprintf("%d %d\n", now, t); getans(now, t); // return 0; cout << ans << '\n'; return0; } /* 7 9 1 3 1 2 a 2 5 c 5 2 d 5 7 b 5 3 a 4 2 b 2 3 a 3 3 e 3 6 a */