Codeforces 679E Bear and Bad Powers of 42

不难发现数列中的元素不可能达到很大,我们只需要考虑 424242 的前若干个幂。

考虑没有赋值操作的做法,则可用线段树维护区间中最接近 424242 的下一个幂的数与这个幂的差值,在区间加时,只需要在线段树上 DFS 找到差值变负的位置更新其与 424242 下一个幂的差值即可。

由于一个数导致进行 DFS 的次数是 O(Log42V)O(Log_{42}V)O(Log42​V) 的,因此这个算法的复杂度是有保证的。

存在赋值操作时,我们会将区间中整段的元素赋为同一个数。

注意到区间加操作至多破开两个整段,其余整段仍然可以被看做是整体,只需要再额外处理一下满足所有数都相同的区间即可保证复杂度正确。

时间复杂度 O((N+Q)LogN×Log42V)O((N+Q)LogN\times Log_{42}V)O((N+Q)LogN×Log42​V) 。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {x = 0; int f = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';x *= f;
}
const ll keys[11] = {1, 42, 1764, 74088, 3111696, 130691232, 5489031744ll, 230539333248ll, 9682651996416ll, 406671383849472ll, 17080198121677824ll};
ll nxt(ll x) {return (*upper_bound(keys, keys + 11, x)) - x; }
struct SegmentTree {struct Node {int lc, rc;ll tag, all, Min;} a[MAXN * 2];int n, root, size;void update(int root) {a[root].Min = min(a[a[root].lc].Min, a[a[root].rc].Min);a[root].all = (a[a[root].lc].all == a[a[root].rc].all) ? a[a[root].lc].all : 0;}void build(int &root, int l, int r, int *b) {root = ++size;if (l == r) {a[root].all = b[l];a[root].Min = nxt(b[l]);return;}int mid = (l + r) / 2;build(a[root].lc, l, mid, b);build(a[root].rc, mid + 1, r, b);update(root);}void init(int x, int *b) {n = x, root = size = 0;build(root, 1, n, b);}void pushdown(int root) {if (a[root].all) {a[a[root].lc].all = a[root].all;a[a[root].lc].Min = a[root].Min;a[a[root].rc].all = a[root].all;a[a[root].rc].Min = a[root].Min;} else if (a[root].tag) {if (a[a[root].lc].all) a[a[root].lc].all += a[root].tag;else a[a[root].lc].tag += a[root].tag;a[a[root].lc].Min -= a[root].tag;if (a[a[root].rc].all) a[a[root].rc].all += a[root].tag;else a[a[root].rc].tag += a[root].tag;a[a[root].rc].Min -= a[root].tag;}a[root].tag = 0;}bool modify(int root, int l, int r, int ql, int qr, int d) {if (l == ql && r == qr) {if (a[root].Min > d) {a[root].Min -= d;if (a[root].all) a[root].all += d;else a[root].tag += d;return false;}if (l == r || a[root].all) {bool ans = false;if (nxt(a[root].all + d - 1) == 1) ans = true;a[root].all += d;a[root].Min = nxt(a[root].all);return ans;}pushdown(root);int mid = (l + r) / 2; bool ans = false;ans |= modify(a[root].lc, l, mid, l, mid, d);ans |= modify(a[root].rc, mid + 1, r, mid + 1, r, d);update(root);return ans;}pushdown(root);int mid = (l + r) / 2; bool ans = false;if (mid >= ql) ans |= modify(a[root].lc, l, mid, ql, min(mid, qr), d);if (mid + 1 <= qr) ans |= modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, d);update(root);return ans;}void modify(int l, int r, int d) {bool flg = true;while (flg) flg = modify(root, 1, n, l, r, d);}void gvalue(int root, int l, int r, int ql, int qr, int d) {if (l == ql && r == qr) {a[root].all = d;a[root].Min = nxt(d);return;}pushdown(root);int mid = (l + r) / 2;if (mid >= ql) gvalue(a[root].lc, l, mid, ql, min(mid, qr), d);if (mid + 1 <= qr) gvalue(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, d);update(root);}void gvalue(int l, int r, int d) {gvalue(root, 1, n, l, r, d);}ll query(int root, int l, int r, int pos) {if (l == r) return a[root].all;int mid = (l + r) / 2; pushdown(root);if (mid >= pos) return query(a[root].lc, l, mid, pos);else return query(a[root].rc, mid + 1, r, pos);update(root);}ll query(int pos) {return query(root, 1, n, pos);}
} ST;
int n, m, a[MAXN];
int main() {read(n), read(m);for (int i = 1; i <= n; i++)read(a[i]);ST.init(n, a);for (int i = 1; i <= m; i++) {int opt; read(opt);if (opt == 1) {int x; read(x);printf("%lld\n", ST.query(x));} else if (opt == 2) {int l, r, x; read(l), read(r), read(x);ST.gvalue(l, r, x);} else {int l, r, x; read(l), read(r), read(x);ST.modify(l, r, x);}}return 0;
}

Codeforces 685C Optimal Point

二分答案,考虑如何判断。

注意到不等式 ∣x−y∣≤z|x-y|\leq z∣x−y∣≤z 等价于 x−y≤z,y−x≤zx-y\leq z,y-x\leq zx−y≤z,y−x≤z ,可以将限制拆分为 232^323 个不含绝对值的限制。

因此,我们可以得到 x+y+z,x+y−z,x−y+z,x−y−zx+y+z,x+y-z,x-y+z,x-y-zx+y+z,x+y−z,x−y+z,x−y−z 的取值范围。

首先,对于确定的 x+y+z,x+y−z,x−y+z,x−y−zx+y+z,x+y-z,x-y+z,x-y-zx+y+z,x+y−z,x−y+z,x−y−z ,只需要它们奇偶性相同便可以得到一组整数解,可以先枚举这个奇偶性。

由 2z=(x+y+z)−(x+y−z)=(x−y+z)−(x−y−z)2z=(x+y+z)-(x+y-z)=(x-y+z)-(x-y-z)2z=(x+y+z)−(x+y−z)=(x−y+z)−(x−y−z) ,我们可以得到 zzz 的取值范围,若为空,则显然无解,否则,取任意一个 zzz 的值代入计算即可得到一组合法解。

时间复杂度 O(∑NLogV)O(\sum NLogV)O(∑NLogV) 。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const long long INF = 4e18;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {x = 0; int f = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';x *= f;
}
int n; ll rx, ry, rz, x[MAXN], y[MAXN], z[MAXN];
bool found(pair <ll, ll> pp, pair <ll, ll> pm, pair <ll, ll> mp, pair <ll, ll> mm, bool parity) {if ((pp.first % 2 == 0) ^ parity) pp.first++;if ((pm.first % 2 == 0) ^ parity) pm.first++;if ((mp.first % 2 == 0) ^ parity) mp.first++;if ((mm.first % 2 == 0) ^ parity) mm.first++;if ((pp.second % 2 == 0) ^ parity) pp.second--;if ((pm.second % 2 == 0) ^ parity) pm.second--;if ((mp.second % 2 == 0) ^ parity) mp.second--;if ((mm.second % 2 == 0) ^ parity) mm.second--;if (pp.first > pp.second) return false;if (pm.first > pm.second) return false;if (mp.first > mp.second) return false;if (mm.first > mm.second) return false;pair <ll, ll> rng = make_pair(-INF, INF);chkmax(rng.first, pp.first - pm.second);chkmin(rng.second, pp.second - pm.first);chkmax(rng.first, mp.first - mm.second);chkmin(rng.second, mp.second - mm.first);if (rng.first > rng.second) return false;rng.first /= 2, rng.second /= 2, rz = rng.first;pair <ll, ll> p = make_pair(-INF, INF);chkmax(p.first, pp.first - rz);chkmin(p.second, pp.second - rz);chkmax(p.first, pm.first + rz);chkmin(p.second, pm.second + rz);pair <ll, ll> m = make_pair(-INF, INF);chkmax(m.first, mp.first - rz);chkmin(m.second, mp.second - rz);chkmax(m.first, mm.first + rz);chkmin(m.second, mm.second + rz);rx = p.first / 2 + m.first / 2;if (p.first & 1) {if (p.first > 0 && m.first > 0) rx++;if (p.first < 0 && m.first < 0) rx--;}ry = p.first - rx;return true;
}
bool check(ll mid) {pair <ll, ll> pp = make_pair(-INF, INF), pm = make_pair(-INF, INF);pair <ll, ll> mp = make_pair(-INF, INF), mm = make_pair(-INF, INF);for (int i = 1; i <= n; i++) {chkmax(pp.first, x[i] + y[i] + z[i] - mid);chkmin(pp.second, x[i] + y[i] + z[i] + mid);chkmax(pm.first, x[i] + y[i] - z[i] - mid);chkmin(pm.second, x[i] + y[i] - z[i] + mid);chkmax(mp.first, x[i] - y[i] + z[i] - mid);chkmin(mp.second, x[i] - y[i] + z[i] + mid);chkmax(mm.first, x[i] - y[i] - z[i] - mid);chkmin(mm.second, x[i] - y[i] - z[i] + mid);}if (found(pp, pm, mp, mm, 0)) return true;if (found(pp, pm, mp, mm, 1)) return true;return false;
}
int main() {int T; read(T);while (T--) {read(n);for (int i = 1; i <= n; i++)read(x[i]), read(y[i]), read(z[i]);ll l = 0, r = 3e18;while (l < r) {ll mid = (l + r) / 2;if (check(mid)) r = mid;else l = mid + 1;}check(l), printf("%lld %lld %lld\n", rx, ry, rz);}return 0;
}

Codeforces 696F …Dary!

二分答案,考虑如何判定答案可行。

对于一个确定的边集,要求再凸多边形中选择一个点,覆盖这个边集,可以用半平面交求出这个点的取值范围,或是判定无解。那么,剩余的问题便在于如何将多边形的边恰当地分为两个边集。

**引理:**只需要考虑将边分为两个环上的区间的情况

**证明:**假设选中了多边形内的两个点,对坐标系进行平移、旋转,使得这两个点均在 X 轴上,且关于原点对称。那么,对于每一条边所在的直线 lll ,若 lll 与 X 轴交于正半轴,则更靠近正半轴的点,否则,其更靠近负半轴的点,由多边形的凸性,更靠近两点之一的边是一个区间。

那么,只需要用双指针计算出对于每个左端点 lll ,最大的 rrr 使得边集 [l,r][l,r][l,r] 可以被覆盖,即可判断。

在计算半平面交时采用合并有序表排序,时间复杂度 O(N2LogV)O(N^2LogV)O(N2LogV) 。

以下代码没有使用合并有序表排序,时间复杂度 O(N2LogNLogV)O(N^2LogNLogV)O(N2LogNLogV) 。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2005;
const double eps = 1e-10;
const double pi = acos(-1);
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {x = 0; int f = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';x *= f;
}
struct point {double x, y; };
struct line {point a, b; double alpha; };
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point operator * (point a, double b) {return (point) {a.x * b, a.y * b}; }
double operator * (point a, point b) {return a.x * b.y - a.y * b.x; }
double moo(point a) {return sqrt(a.x * a.x + a.y * a.y); }
double PolarAngle(point a) {return atan2(a.y, a.x); }
point unit(point a) {return a * (1.0 / moo(a)); }
point intersect(const line &x, const line &y) {double tmp = (y.a - x.a) * (y.b - x.a);double tnp = (y.b - x.b) * (y.a - x.b);return (x.a * tnp + x.b * tmp) * (1 / (tmp + tnp));
}
bool onright(const line &l, const point &p) {if ((p - l.a) * (l.b - l.a) > -eps) return true;else return false;
}
bool onrightcmp(const line &l, const point &p) {if ((p - l.a) * (l.b - l.a) > eps) return true;else return false;
}
bool cmp(const line &a, const line &b) {if (fabs(a.alpha - b.alpha) > eps) return a.alpha < b.alpha;else return onrightcmp(a, b.a);
}
bool HalfPlane(int n, line *a, point &res) {static point p[MAXN];static line  q[MAXN];sort(a + 1, a + n + 1, cmp);int l = 0, r = -1;for (int i = 1; i <= n; i++) {while (l < r && onright(a[i], p[r])) r--;while (l < r && onright(a[i], p[l + 1])) l++;if (fabs(a[i].alpha - q[r].alpha) <= eps) continue;if (a[i].alpha - q[r].alpha >= pi - eps) return false;q[++r] = a[i];if (l < r) p[r] = intersect(q[r], q[r - 1]);}while (l < r && onright(q[l], p[r])) r--;while (l < r && onright(q[r], p[l + 1])) l++;if (r - l <= 1) return false;res = intersect(q[l], q[r]);return true;
}
int n; point a[MAXN]; line b[MAXN]; point vec[MAXN]; double p[MAXN];
bool check(int l, int r, double d, point &res) {static line c[MAXN]; int tot = 0;for (int i = 1; i <= n; i++)c[++tot] = b[i];for (int i = l; i <= r; i++)c[++tot] = (line) {a[i + 1] + vec[i] * d, a[i] + vec[i] * d, p[i]};return HalfPlane(tot, c, res);
}
bool check(double mid) {static int nxt[MAXN]; int pos = 1; point tmp;for (int i = 1; i <= 2 * n; i++) {while (pos < 2 * n && check(i, pos + 1, mid, tmp)) pos++;nxt[i] = pos;}nxt[2 * n + 1] = 2 * n + 1;for (int i = 1; i <= 2 * n; i++)if (nxt[nxt[i] + 1] >= i + n - 1) return true;return false;
}
void getans(double mid) {static int nxt[MAXN]; int pos = 1;static point ans[MAXN]; point tmp;for (int i = 1; i <= 2 * n; i++) {while (pos < 2 * n && check(i, pos + 1, mid, tmp)) pos++;nxt[i] = pos; assert(check(i, pos, mid, ans[i]));}nxt[2 * n + 1] = 2 * n + 1;for (int i = 1; i <= 2 * n; i++)if (nxt[nxt[i] + 1] >= i + n - 1) {printf("%.10lf %.10lf\n", ans[i].x, ans[i].y);printf("%.10lf %.10lf\n", ans[nxt[i] + 1].x, ans[nxt[i] + 1].y);return;}assert(false);
}
int main() {read(n);for (int i = 1; i <= n; i++) {read(a[i].x), read(a[i].y);a[i + n] = a[i];}a[n * 2 + 1] = a[1];for (int i = 1; i <= 2 * n; i++) {b[i] = (line) {a[i], a[i + 1], PolarAngle(a[i + 1] - a[i])};vec[i] = a[i + 1] - a[i];swap(vec[i].x, vec[i].y), vec[i].x *= -1;vec[i] = vec[i] * (1 / moo(vec[i]));p[i] = PolarAngle(a[i] - a[i + 1]);}double l = 0, r = 1e5;while (l + eps < r) {double mid = (l + r) / 2;if (check(mid)) r = mid;else l = mid;}printf("%.10lf\n", r);getans(r);return 0;
}

Codeforces 698D Limak and Shooting Points

将怪物点集对各个传送位置进行极角排序,处理出各个怪物与传送位置连线上下一个怪物。

考虑每个怪物是否能被击中,则对于这只需要击中的怪物,可以枚举攻击它的位置,将连线上的怪物加入需要攻击的集合,由此搜索即可。

时间复杂度 O(N×k!×k)O(N\times k!\times k)O(N×k!×k) 。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 15;
const int MAXM = 1005;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {x = 0; int f = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';x *= f;
}
struct point {int x, y; };
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point operator * (point a, int b) {return (point) {a.x * b, a.y * b}; }
long long operator * (point a, point b) {return 1ll * a.x * b.y - 1ll * a.y * b.x; }
long long moo(point a) {return 1ll * a.x * a.x + 1ll * a.y * a.y; }
point a[MAXM], p[MAXM]; bool flg, visa[MAXN], visp[MAXM];
int n, m, top, res[MAXN], nxt[MAXN][MAXM];
pair <point, int> b[MAXM];
void work(int depth) {if (top > n) return;if (depth > top) {flg = true;return;}for (int i = 1; i <= n; i++)if (!visa[i]) {visa[i] = true;int old = top, pos = nxt[i][res[depth]];while (pos != 0 && top <= n) {if (!visp[pos]) {visp[pos] = true;res[++top] = pos;}pos = nxt[i][pos];}work(depth + 1);while (top > old) visp[res[top--]] = false;visa[i] = false;}
}
bool where(point a) {return a.x > 0 || (a.x == 0 && a.y > 0);
}
bool cmp(pair <point, int> a, pair <point, int> b) {if (where(a.first) == where(b.first)) {if (a.first * b.first == 0) return moo(a.first) > moo(b.first);else return a.first * b.first > 0;} else return where(a.first) < where(b.first);
}
int main() {read(n), read(m);for (int i = 1; i <= n; i++)read(a[i].x), read(a[i].y);for (int i = 1; i <= m; i++)read(p[i].x), read(p[i].y);for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++)b[j] = make_pair(p[j] - a[i], j);sort(b + 1, b + m + 1, cmp);for (int j = 1; j <= m - 1; j++)if (where(b[j].first) == where(b[j + 1].first) && b[j].first * b[j + 1].first == 0)nxt[i][b[j].second] = b[j + 1].second;}int ans = 0;for (int i = 1; i <= m; i++) {flg = false;visp[i] = true;res[top = 1] = i;work(1), ans += flg;visp[i] = false;}printf("%d\n", ans);return 0;
}

Codeforces 700E Cool Slogans

不计复杂度,考虑如下做法:

枚举 xxx ,令 s1s_1s1​ 为第 xxx 个字符,每次找到 sis_isi​ 的下一个出现位置 si′s_i'si′​ ,令 si+1s_{i+1}si+1​ 为恰好包含 si,si′s_i,s_i'si​,si′​ 的字符串,直到不存在 si′s_i'si′​ ,对所达到的 iii 取最大值输出。

这个做法是正确的,因为对于任意的 si+1s_{i+1}si+1​ ,若 si,si′s_i,s_{i}'si​,si′​ 不分别是 si+1s_{i+1}si+1​ 的前、后缀,则可以将 si+1s_{i+1}si+1​ 缩短,从而可以认为 sis_isi​ 是 si+1s_{i+1}si+1​ 的一个 border ,上面枚举的 xxx 相当于枚举了最终串的开头位置。

考虑用后缀数组优化这个做法,由字符串周期理论,字符串的 border 是由至多 O(LogN)O(LogN)O(LogN) 段等差数列构成的,也就是说,我们只要能够较快地处理每一段等差数列即可。

那么,我们只需要支持找到 si′s_i'si′​ ,计算 sis_isi​ 开头的后缀和 si′s_i'si′​ 开头的后缀的 LCP 即可,可以分别用后缀数组和可持久化线段树解决。

时间复杂度 O(NLog2N)O(NLog^2N)O(NLog2N) 。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXP = 5e6 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {x = 0; int f = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';x *= f;
}
namespace SuffixArray {const int MAXN = 2e5 + 5;const int MAXLOG = 20;const int MAXC = 256;int sa[MAXN], rnk[MAXN], height[MAXN];int Min[MAXN][MAXLOG], bit[MAXN], N;void init(char *a, int n) {N = n;static int x[MAXN], y[MAXN], cnt[MAXN], rk[MAXN];memset(cnt, 0, sizeof(cnt));for (int i = 1; i <= n; i++)cnt[(int) a[i]]++;for (int i = 1; i <= MAXC; i++)cnt[i] += cnt[i - 1];for (int i = n; i >= 1; i--)sa[cnt[(int) a[i]]--] = i;rnk[sa[1]] = 1;for (int i = 2; i <= n; i++)rnk[sa[i]] = rnk[sa[i - 1]] + (a[sa[i]] != a[sa[i - 1]]);for (int k = 1; rnk[sa[n]] != n; k <<= 1) {for (int i = 1; i <= n; i++) {x[i] = rnk[i];y[i] = (i + k <= n) ? rnk[i + k] : 0;}memset(cnt, 0, sizeof(cnt));for (int i = 1; i <= n; i++)cnt[y[i]]++;for (int i = 1; i <= n; i++)cnt[i] += cnt[i - 1];for (int i = n; i >= 1; i--)rk[cnt[y[i]]--] = i;memset(cnt, 0, sizeof(cnt));for (int i = 1; i <= n; i++)cnt[x[i]]++;for (int i = 1; i <= n; i++)cnt[i] += cnt[i - 1];for (int i = n; i >= 1; i--)sa[cnt[x[rk[i]]]--] = rk[i];rnk[sa[1]] = 1;for (int i = 2; i <= n; i++)rnk[sa[i]] = rnk[sa[i - 1]] + (x[sa[i]] != x[sa[i - 1]] || y[sa[i]] != y[sa[i - 1]]);      }int now = 0;for (int i = 1; i <= n; i++) {if (now) now--;while (a[i + now] == a[sa[rnk[i] + 1] + now]) now++;height[rnk[i]] = now;}for (int i = 1; i <= n; i++)Min[i][0] = height[i];for (int p = 1; p < MAXLOG; p++) {int tmp = 1 << (p - 1);for (int i = 1, j = tmp + 1; j <= n; i++, j++)Min[i][p] = min(Min[i][p - 1], Min[i + tmp][p - 1]);}for (int i = 1; i <= n; i++) {bit[i] = bit[i - 1];if (i >= 1 << (bit[i] + 1)) bit[i]++;}}int lcp(int x, int y) {if (x == y) return N - x + 1;x = rnk[x], y = rnk[y];if (x > y) swap(x, y);int tmp = bit[y - x];return min(Min[x][tmp], Min[y - (1 << tmp)][tmp]);}
}
struct SegmentTree {struct Node {int lc, rc;int sum;} a[MAXP];int n, size, root[MAXN];void build(int &root, int l, int r) {if (root == 0) root = ++size;if (l == r) return;int mid = (l + r) / 2;build(a[root].lc, l, mid);build(a[root].rc, mid + 1, r);}void init(int x) {n = x; size = 0;build(root[0], 1, n);}int modify(int root, int l, int r, int pos, int delta) {int ans = ++size;a[ans] = a[root];a[ans].sum += delta;if (l == r) return ans;int mid = (l + r) / 2;if (mid >= pos) a[ans].lc = modify(a[root].lc, l, mid, pos, delta);else a[ans].rc = modify(a[root].rc, mid + 1, r, pos, delta);return ans;}void extend(int ver, int pos, int delta) {root[ver] = modify(root[ver - 1], 1, n, pos, delta);}int findnxt(int rootl, int rootr, int l, int r, int pos) {if (a[rootr].sum - a[rootl].sum == 0 || r <= pos) return -1;if (l == r) return l;int mid = (l + r) / 2, ans = -1;ans = findnxt(a[rootl].lc, a[rootr].lc, l, mid, pos);if (ans != -1) return ans;ans = findnxt(a[rootl].rc, a[rootr].rc, mid + 1, r, pos);return ans;}int findnxt(int l, int r, int pos) {return findnxt(root[l - 1], root[r], 1, n, pos);}
} ST;
int n; char s[MAXN];
int getans(int l) {int len = 1, ans = 1;using namespace SuffixArray;while (true) {int ql = 1, qr = rnk[l];while (ql < qr) {int mid = (ql + qr) / 2;if (lcp(sa[mid], l) >= len) qr = mid;else ql = mid + 1;}int tl = ql;ql = rnk[l], qr = n;while (ql < qr) {int mid = (ql + qr + 1) / 2;if (lcp(sa[mid], l) >= len) ql = mid;else qr = mid - 1;}int tr = ql;int nxt = ST.findnxt(tl, tr, l);if (nxt == -1) return ans;int lcpnxt = lcp(l, nxt), delta = nxt - l;int cnt = (lcpnxt + delta - len) / delta;ans += cnt, len += cnt * delta;}return -1;
}
int main() {read(n), scanf("\n%s", s + 1);SuffixArray :: init(s, n), ST.init(n);using namespace SuffixArray;for (int i = 1; i <= n; i++)ST.extend(i, sa[i], 1);int ans = 0;for (int i = 1; i <= n; i++)chkmax(ans, getans(i));printf("%d\n", ans);return 0;
}

Codeforces 704B Ant Man

对于 s>ts>ts>t 的情况,可以翻转序列,下令 s<ts<ts<t 。

代价中 ∣xi−xj∣|x_i-x_j|∣xi​−xj​∣ 的部分可以计入 ai,bi,ci,dia_i,b_i,c_i,d_iai​,bi​,ci​,di​ ,从而不考虑这部分代价。

考虑从左向右 DP , iii 号点左侧路径的轮廓线是由至多一个路径开头、一个路径结尾和若干条子路径组成的,路径开头在 i>si>si>s 时出现,路径结尾在 i<ti<ti<t 时出现。

因此,在状态中计入子路径的条数,即可 O(1)O(1)O(1) 转移。

时间复杂度 O(N2)O(N^2)O(N2) 。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5005;
const long long INF = 1e18;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {x = 0; int f = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';x *= f;
}
int n, s, t; ll dp[MAXN][MAXN];
int x[MAXN], a[MAXN], b[MAXN], c[MAXN], d[MAXN];
int main() {read(n), read(s), read(t);for (int i = 1; i <= n; i++)read(x[i]);for (int i = 1; i <= n; i++)read(a[i]);for (int i = 1; i <= n; i++)read(b[i]);for (int i = 1; i <= n; i++)read(c[i]);for (int i = 1; i <= n; i++)read(d[i]);if (s > t) {s = n - s + 1;t = n - t + 1;for (int i = 1; i <= n; i++)x[i] = x[n] - x[i];reverse(x + 1, x + n + 1);reverse(a + 1, a + n + 1);reverse(b + 1, b + n + 1);reverse(c + 1, c + n + 1);reverse(d + 1, d + n + 1);swap(a, b), swap(c, d);}for (int i = 1; i <= n; i++) {a[i] += x[i];b[i] -= x[i];c[i] += x[i];d[i] -= x[i];}for (int i = 1; i <= n + 1; i++)for (int j = 0; j <= n; j++)dp[i][j] = INF;dp[1][0] = 0;for (int i = 1; i <= n; i++) {if (i == s) {for (int j = 1 - (i == 1); j <= n; j++) {chkmin(dp[i + 1][j + 1], dp[i][j] + d[i]);if (j != 0) chkmin(dp[i + 1][j], dp[i][j] + c[i]);}} else if (i == t) {for (int j = 1 - (i == 1); j <= n; j++) {chkmin(dp[i + 1][j], dp[i][j] + b[i]);if (j != 0) chkmin(dp[i + 1][j - 1], dp[i][j] + a[i]);}} else {for (int j = 1 - (i == 1); j <= n; j++) {chkmin(dp[i + 1][j + 1], dp[i][j] + b[i] + d[i]);if (j != 0) {chkmin(dp[i + 1][j], dp[i][j] + a[i] + d[i]);chkmin(dp[i + 1][j - 1], dp[i][j] + a[i] + c[i]);if (j != 1 || i < s || i > t) chkmin(dp[i + 1][j], dp[i][j] + b[i] + c[i]);}}}}cout << dp[n + 1][0] << endl;return 0;
}

Codeforces 704C Black Widow

考虑将 k=2k=2k=2 的限制看做图中的一条边,则图中只存在路径和环。

分别在路径和环上 DP 即可。

时间复杂度 O(N+M)O(N+M)O(N+M) 。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int P = 1e9 + 7;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {x = 0; int f = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';x *= f;
}
struct info {int dest, home;bool x, y;
};
vector <info> a[MAXN];
int n, m, ans[2], cur[2][2][2];
bool vis[MAXN], res[MAXN][2];
void update(int &x, int y) {x += y;if (x >= P) x -= P;
}
void work(int pos, int fa) {vis[pos] = true;for (auto x : a[pos])if (x.home != fa) {if (vis[x.dest]) {int tmp[2] = {0, 0};for (int i = 0; i <= 1; i++)for (int j = 0; j <= 1; j++)for (int k = 0; k <= 1; k++)update(tmp[k ^ ((j ^ x.x) || (i ^ x.y))], cur[i][j][k]);int res[2] = {0, 0};for (int i = 0; i <= 1; i++)for (int j = 0; j <= 1; j++)update(res[i ^ j], 1ll * tmp[i] * ans[j] % P);ans[0] = res[0];ans[1] = res[1];} else {int nxt[2][2][2];memset(nxt, 0, sizeof(nxt));for (int i = 0; i <= 1; i++)for (int j = 0; j <= 1; j++)for (int k = 0; k <= 1; k++)for (int t = 0; t <= 1; t++)update(nxt[i][t][k ^ res[x.dest][t ^ 1] ^ ((j ^ x.x) || (t ^ x.y))], cur[i][j][k]);memcpy(cur, nxt, sizeof(nxt));work(x.dest, x.home);}return;}int tmp[2] = {0, 0};for (int i = 0; i <= 1; i++)for (int j = 0; j <= 1; j++)for (int k = 0; k <= 1; k++)update(tmp[k], cur[i][j][k]);int res[2] = {0, 0};for (int i = 0; i <= 1; i++)for (int j = 0; j <= 1; j++)update(res[i ^ j], 1ll * tmp[i] * ans[j] % P);ans[0] = res[0];ans[1] = res[1];
}
int main() {read(n), read(m);for (int i = 1; i <= n; i++) {int k, x[3] = {0, 0, 0}; read(k);for (int j = 1; j <= k; j++)read(x[j]);if (k == 1) res[abs(x[1])][x[1] < 0] ^= true;else {int b = abs(x[1]), c = abs(x[2]);a[b].push_back((info) {c, i, x[1] < 0, x[2] < 0});a[c].push_back((info) {b, i, x[2] < 0, x[1] < 0});}}ans[0] = 1, ans[1] = 0;for (int i = 1; i <= m; i++)if (!vis[i] && a[i].size() <= 1) {memset(cur, 0, sizeof(cur));cur[0][0][res[i][1]] = 1;cur[1][1][res[i][0]] = 1;work(i, 0);}for (int i = 1; i <= m; i++)if (!vis[i]) {memset(cur, 0, sizeof(cur));cur[0][0][res[i][1]] = 1;cur[1][1][res[i][0]] = 1;work(i, 0);}cout << ans[1] << endl;return 0;
}

Codeforces 704D Captain America

首先,定义 ttt 次成功率为 ppp 的操作恰好成功 xxx 次的概率为 ft,p(x)f_{t,p}(x)ft,p​(x) ,有 ft,p(x)=px(1−p)t−x(tx)f_{t,p}(x)=p^x(1-p)^{t-x}\binom{t}{x}ft,p​(x)=px(1−p)t−x(xt​) 。

对于任意一行,留下的部分是区间 [l,r][l,r][l,r] 的概率为 ft,p(l−1)×ft,p(M−r)f_{t,p}(l-1)\times f_{t,p}(M-r)ft,p​(l−1)×ft,p​(M−r) 。

考虑一个直观的 O(NM2)O(NM^2)O(NM2) 的动态规划做法,记 dpi,j,kdp_{i,j,k}dpi,j,k​ 表示前 iii 行连通,并且第 iii 行留下的区间为 [j,k][j,k][j,k] 的概率,记 prei,x=∑j=1x∑k=jxdpi,j,k,sufi,x=∑j=xM∑k=jMdpi,j,kpre_{i,x}=\sum_{j=1}^{x}\sum_{k=j}^{x}dp_{i,j,k},suf_{i,x}=\sum_{j=x}^{M}\sum_{k=j}^{M}dp_{i,j,k}prei,x​=∑j=1x​∑k=jx​dpi,j,k​,sufi,x​=∑j=xM​∑k=jM​dpi,j,k​ ,显然有转移
dpi,j,k=ft,p(j−1)×ft,p(M−k)×(prei−1,M−prei−1,j−1−sufi−1,k+1)dp_{i,j,k}=f_{t,p}(j-1)\times f_{t,p}(M-k)\times (pre_{i-1,M}-pre_{i-1,j-1}-suf_{i-1,k+1})dpi,j,k​=ft,p​(j−1)×ft,p​(M−k)×(prei−1,M​−prei−1,j−1​−sufi−1,k+1​)

我们发现转移时并不需要知道 dpi,j,kdp_{i,j,k}dpi,j,k​ ,只需要知道 prei,xpre_{i,x}prei,x​ 和 sufi,xsuf_{i,x}sufi,x​ 即可。

考虑直接利用 prei,xpre_{i,x}prei,x​ 和 sufi,xsuf_{i,x}sufi,x​ 进行动态规划,以计算 prei,xpre_{i,x}prei,x​ 为例,记 prei,x′=∑j=1xdpi,j,xpre'_{i,x}=\sum_{j=1}^{x}dp_{i,j,x}prei,x′​=∑j=1x​dpi,j,x​ ,显然 prei,xpre_{i,x}prei,x​ 就是 prei,x′pre'_{i,x}prei,x′​ 的前缀和。那么根据上面的计算式,有
prei,k′=∑j=1kft,p(j−1)×ft,p(M−k)×(prei−1,M−prei−1,j−1−sufi−1,k+1)pre'_{i,k}=\sum_{j=1}^{k}f_{t,p}(j-1)\times f_{t,p}(M-k)\times (pre_{i-1,M}-pre_{i-1,j-1}-suf_{i-1,k+1})prei,k′​=j=1∑k​ft,p​(j−1)×ft,p​(M−k)×(prei−1,M​−prei−1,j−1​−sufi−1,k+1​)

可以发现,若将括号拆开,每一项都可以表示为只和 i,ki,ki,k 相关的一个量乘以只和 i,ji,ji,j 相关的一个量,可以通过部分和进行优化。

时间复杂度为 O(NM+K)O(NM+K)O(NM+K) 。

#include<bits/stdc++.h>
using namespace std;
const int MAXM = 2e5 + 5;
const int MAXN = 3005;
const int P = 1e9 + 7;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {x = 0; int f = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';x *= f;
}
template <typename T> void write(T x) {if (x < 0) x = -x, putchar('-');if (x > 9) write(x / 10);putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {write(x);puts("");
}
int pre[2][MAXN], suf[2][MAXN];
int n, m, p, q, t, lp[MAXN], rp[MAXN];
int fac[MAXM], inv[MAXM], powp[MAXM], powq[MAXM];
void update(int &x, int y) {x = (x + y >= P) ? (x + y - P) : (x + y);
}
void getdp() {static int sumlp[MAXN], sumrp[MAXN];for (int i = 1; i <= m; i++) {sumlp[i] = (sumlp[i - 1] + lp[i]) % P;pre[1][i] = 1ll * sumlp[i] * rp[i] % P;update(pre[1][i], pre[1][i - 1]);}for (int i = m; i >= 1; i--) {sumrp[i] = (sumrp[i + 1] + rp[i]) % P;suf[1][i] = 1ll * sumrp[i] * lp[i] % P;update(suf[1][i], suf[1][i + 1]);}for (int i = 2, now = 0, from = 1; i <= n; i++, swap(now, from)) {int tmp = 0;for (int j = 1; j <= m; j++) {pre[now][j] = 1ll * sumlp[j] * pre[from][m] % P;update(tmp, 1ll * pre[from][j - 1] * lp[j] % P);update(pre[now][j], P - tmp);update(pre[now][j], P - 1ll * sumlp[j] * suf[from][j + 1] % P);pre[now][j] = 1ll * pre[now][j] * rp[j] % P;update(pre[now][j], pre[now][j - 1]);}tmp = 0;for (int j = m; j >= 1; j--) {suf[now][j] = 1ll * sumrp[j] * pre[from][m] % P;update(tmp, 1ll * suf[from][j + 1] * rp[j] % P);update(suf[now][j], P - tmp);update(suf[now][j], P - 1ll * sumrp[j] * pre[from][j - 1] % P);suf[now][j] = 1ll * suf[now][j] * lp[j] % P;update(suf[now][j], suf[now][j + 1]);}}
}
int power(int x, int y) {if (y == 0) return 1;int tmp = power(x, y / 2);if (y % 2 == 0) return 1ll * tmp * tmp % P;else return 1ll * tmp * tmp % P * x % P;
}
int getc(int x, int y) {if (y > x) return 0;else return 1ll * fac[x] * inv[y] % P * inv[x - y] % P;
}
int getp(int x) {return 1ll * getc(t, x) * powp[x] % P * powq[t - x] % P;
}
void init(int n) {fac[0] = powp[0] = powq[0] = 1;for (int i = 1; i <= n; i++) {fac[i] = 1ll * fac[i - 1] * i % P;powp[i] = 1ll * powp[i - 1] * p % P;powq[i] = 1ll * powq[i - 1] * q % P;}inv[n] = power(fac[n], P - 2);for (int i = n - 1; i >= 0; i--)inv[i] = inv[i + 1] * (i + 1ll) % P;for (int i = 1; i <= m; i++) {lp[i] = getp(i - 1);rp[i] = getp(m - i);}
}
int main() {read(n), read(m);read(p), read(q);p = 1ll * p * power(q, P - 2) % P;q = (P + 1 - p) % P;read(t), init(t), getdp();printf("%d\n", pre[n & 1][m]);return 0;
}

Codeforces 704E Iron Man

考虑链上做法,每个人的坐标是一个关于时间的一次函数。

注意到当且仅当两人相碰,两人位置的相对顺序会发生改变,换言之,在两人相碰之前,所有人位置的相对顺序不变。

排序所有人出现,消失的事件,用平衡树维护所有人的相对位置即可。

回到原题,对原树进行树链剖分,对每条重链和轻边都运行链上做法即可。

时间复杂度 O(N+MLog2N)O(N+MLog^2N)O(N+MLog2N) 。

本题中,涉及除法的地方不多,分子和分母的乘积始终在六十四位整型范围内,可以通过手写有理数类来避免实数运算。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int INF = 2e4;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {x = 0; int f = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';x *= f;
}
template <typename T> void write(T x) {if (x < 0) x = -x, putchar('-');if (x > 9) write(x / 10);putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {write(x);puts("");
}
struct frac {ll x, y; };
ll absll(ll x) {return x >= 0 ? x : -x; }
frac make(int x) {return (frac) {x, 1}; }
frac normal(frac x) {ll g = __gcd(absll(x.x), absll(x.y));if (x.y < 0) g = -g;return (frac) {x.x / g, x.y / g};
}
frac operator + (frac a, frac b) {return normal((frac) {a.x * b.y + a.y * b.x, a.y * b.y}); }
frac operator - (frac a, frac b) {return normal((frac) {a.x * b.y - a.y * b.x, a.y * b.y}); }
frac operator * (frac a, frac b) {return normal((frac) {a.x * b.x, a.y * b.y}); }
frac operator / (frac a, frac b) {return normal((frac) {a.x * b.y, a.y * b.x}); }
bool operator < (frac a, frac b) {return a.x * b.y < b.x * a.y; }
bool operator > (frac a, frac b) {return a.x * b.y > b.x * a.y; }
bool operator <= (frac a, frac b) {return a.x * b.y <= b.x * a.y; }
bool operator >= (frac a, frac b) {return a.x * b.y >= b.x * a.y; }
bool operator == (frac a, frac b) {return a.x * b.y == b.x * a.y; }
int n, m, depth[MAXN], size[MAXN], son[MAXN];
int timer, father[MAXN], dfn[MAXN], up[MAXN];
vector <int> a[MAXN]; double ans;
int lca(int x, int y) {while (up[x] != up[y]) {if (depth[up[x]] < depth[up[y]]) swap(x, y);x = father[up[x]];}if (depth[x] < depth[y]) return x;else return y;
}
frac dist(int x, int y) {int z = lca(x, y);return (frac) {depth[x] + depth[y] - 2 * depth[z], 1};
}
struct event {frac k, b, l, r; };
frac Timer, Goal;
bool operator < (event a, event b) {if (a.k * Timer + a.b == b.k * Timer + b.b) {if (a.l == b.l) {if (a.r == b.r) return a.k < b.k;else return a.r < b.r;} else return a.l < b.l;} else return a.k * Timer + a.b < b.k * Timer + b.b;
}
multiset <event> st;
bool cmp(pair <event, bool> x, pair <event, bool> y) {frac tx = x.second ? x.first.l : x.first.r;frac ty = y.second ? y.first.l : y.first.r;if (tx == ty) return x.second > y.second;else return tx < ty;
}
frac inter(event a, event b) {if (a.k == b.k) {if (a.b == b.b) return max(a.l, b.l);else return make(INF);}frac res = (b.b - a.b) / (a.k - b.k);if (res >= max(a.l, b.l) && res <= min(a.r, b.r)) return res;else return make(INF);
}
void work(vector <event> &a) {st.clear(), Goal = make(INF);vector <pair <event, bool>> b;for (auto x : a) {b.emplace_back(x, true);b.emplace_back(x, false);}sort(b.begin(), b.end(), cmp);for (auto x : b) {frac now = x.second ? x.first.l : x.first.r;if (now >= Goal) {chkmin(ans, 1.0 * Goal.x / Goal.y);return;}Timer = now;if (x.second) {multiset <event> :: iterator tmp = st.insert(x.first);multiset <event> :: iterator pre = tmp, suf = tmp;if (pre != st.begin()) {pre--;chkmin(Goal, inter(*pre, *tmp));}if (++suf != st.end()) chkmin(Goal, inter(*tmp, *suf));} else {multiset <event> :: iterator tmp = st.lower_bound(x.first);multiset <event> :: iterator pre = tmp, suf = tmp;if (++suf != st.end() && pre != st.begin()) {pre--;chkmin(Goal, inter(*pre, *suf));}st.erase(tmp);}}chkmin(ans, 1.0 * Goal.x / Goal.y);
}
vector <event> heavy[MAXN], light[MAXN];
void addquery(int x, int y, frac s, frac v) {frac t = s + dist(x, y) / v;while (up[x] != up[y]) {if (depth[up[x]] > depth[up[y]]) {int f = up[x];heavy[f].push_back((event) {make(0) - v, make(depth[x]) + v * s, s, s + make(depth[x] - depth[f]) / v});s = s + make(depth[x] - depth[f]) / v;x = up[x], f = father[x];light[x].push_back((event) {make(0) - v, make(depth[x]) + v * s, s, s + make(depth[x] - depth[f]) / v});s = s + make(depth[x] - depth[f]) / v;x = father[x];} else {int f = up[y];heavy[f].push_back((event) {v, make(depth[y]) - v * t, t - make(depth[y] - depth[f]) / v, t});t = t - make(depth[y] - depth[f]) / v;y = up[y], f = father[y];light[y].push_back((event) {v, make(depth[y]) - v * t, t - make(depth[y] - depth[f]) / v, t});t = t - make(depth[y] - depth[f]) / v;y = father[y];}}int f = up[x];if (depth[x] > depth[y]) heavy[f].push_back((event) {make(0) - v, make(depth[x]) + v * s, s, t});else heavy[f].push_back((event) {v, make(depth[y]) - v * t, s, t});
}
void dfs(int pos, int fa) {depth[pos] = depth[fa] + 1;size[pos] = 1, son[pos] = 0;for (auto x : a[pos])if (x != fa) {dfs(x, pos);size[pos] += size[x];if (size[x] > size[son[pos]]) son[pos] = x;}
}
void efs(int pos, int fa, int from) {up[pos] = from;father[pos] = fa;dfn[pos] = ++timer;if (son[pos]) efs(son[pos], pos, from);for (auto x : a[pos])if (x != son[pos] && x != fa)efs(x, pos, x);
}
int main() {read(n), read(m);for (int i = 1; i <= n - 1; i++) {int x, y; read(x), read(y);a[x].push_back(y);a[y].push_back(x);}dfs(1, 0);efs(1, 0, 1);for (int i = 1; i <= m; i++) {frac t, v; int x, y;read(t.x), t.y = 1;read(v.x), v.y = 1;read(x), read(y);addquery(x, y, t, v);}ans = INF;for (int i = 1; i <= n; i++) {work(heavy[i]);work(light[i]);}if (ans >= INF) puts("-1");else printf("%.10lf\n", ans);return 0;
}

Codeforces 708D Incorrect Flow

设立超级源点 ssssss ,超级汇点 ststst 。计算各个点出入度的差 did_idi​ ,若为正则令 ssssss 连向 iii ,若为负则令 iii 连向 ststst ,费用为 000 ,容量为 ∣di∣|d_i|∣di​∣ 。

我们的目标是超级源到超级汇满流。

对于原图中的每一条边:

(1)(1)(1) 、若 f≤cf\leq cf≤c :
则将 fff 改小的代价是 111 ,上限为 fff ;
将 fff 改大的代价是 111 ,上限为 c−fc-fc−f ;
将 fff 进一步改大的代价是 222 ,上限为 +∞+\infty+∞ 。

(2)(2)(2) 、若 f>cf>cf>c :
则必然存在的花费为 f−cf-cf−c ;
将 fff 改小的代价是 000 ,上限为 f−cf-cf−c ;
将 fff 进一步改小的代价是 111 ,上限为 ccc ;
将 fff 改大的代价是 222 ,上限为 +∞+\infty+∞ 。

再增设一条汇点到源点的边,容量无穷,费用为零。

用最小费用最大流计算答案即可。

时间复杂度 O(MinCostFlow(N,M))O(MinCostFlow(N,M))O(MinCostFlow(N,M)) 。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
const int INF  = 1e9 + 7;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {x = 0; int f = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';x *= f;
}
namespace MincostFlow {const int MAXP = 105;const int MAXQ = 2e4 + 5;const int INF  = 2e9;struct edge {int dest, flow, pos, cost; };vector <edge> a[MAXP];int n, m, s, t, tot, flow; ll cost;int dist[MAXP], path[MAXP], home[MAXP];void FlowPath() {int p = t, ans = INF;while (p != s) {ans = min(ans, a[path[p]][home[p]].flow);p = path[p];}flow += ans;cost += 1ll * ans * dist[t];p = t;while (p != s) {a[path[p]][home[p]].flow -= ans;a[p][a[path[p]][home[p]].pos].flow += ans;p = path[p];}}bool spfa() {static int q[MAXQ];static bool inq[MAXP];static int l = 0, r = 0;for (int i = 0; i <= r; i++)dist[q[i]] = INF;q[l = r = 0] = s, dist[s] = 0, inq[s] = true;while (l <= r) {int tmp = q[l];for (unsigned i = 0; i < a[tmp].size(); i++)if (a[tmp][i].flow != 0 && dist[tmp] + a[tmp][i].cost < dist[a[tmp][i].dest]) {dist[a[tmp][i].dest] = dist[tmp] + a[tmp][i].cost;path[a[tmp][i].dest] = tmp;home[a[tmp][i].dest] = i;if (!inq[a[tmp][i].dest]) {q[++r] = a[tmp][i].dest;inq[q[r]] = true;}}l++, inq[tmp] = false;}return dist[t] != INF;}void addedge(int x, int y, int z, int c) {a[x].push_back((edge){y, z, a[y].size(), c});a[y].push_back((edge){x, 0, a[x].size() - 1, -c});}ll work(int n, int x, int y) {s = x, t = y;for (int i = 1; i <= n; i++)dist[i] = INF;while (spfa()) FlowPath();return cost;}
}
int n, m, x[MAXN], y[MAXN], d[MAXN], c[MAXN], f[MAXN];
int main() {read(n), read(m);for (int i = 1; i <= m; i++) {read(x[i]), read(y[i]);read(c[i]), read(f[i]);d[x[i]] -= f[i];d[y[i]] += f[i];}int s = n + 1, t = n + 2;MincostFlow :: addedge(n, 1, INF, 0);for (int i = 1; i <= n; i++) {if (d[i] > 0) MincostFlow :: addedge(s, i, d[i], 0);else MincostFlow :: addedge(i, t, -d[i], 0);}ll ans = 0;for (int i = 1; i <= m; i++) {if (f[i] <= c[i]) {MincostFlow :: addedge(y[i], x[i], f[i], 1);MincostFlow :: addedge(x[i], y[i], c[i] - f[i], 1);MincostFlow :: addedge(x[i], y[i], INF, 2);} else {ans += f[i] - c[i];MincostFlow :: addedge(y[i], x[i], c[i], 1);MincostFlow :: addedge(y[i], x[i], f[i] - c[i], 0);MincostFlow :: addedge(x[i], y[i], INF, 2);}}cout << ans + MincostFlow :: work(n + 2, s, t) << endl;return 0;
}

Codeforces 708E Student’s Camp

首先,定义 ttt 次成功率为 ppp 的操作恰好成功 xxx 次的概率为 ft,p(x)f_{t,p}(x)ft,p​(x) ,有 ft,p(x)=px(1−p)t−x(tx)f_{t,p}(x)=p^x(1-p)^{t-x}\binom{t}{x}ft,p​(x)=px(1−p)t−x(xt​) 。

对于任意一行,留下的部分是区间 [l,r][l,r][l,r] 的概率为 ft,p(l−1)×ft,p(M−r)f_{t,p}(l-1)\times f_{t,p}(M-r)ft,p​(l−1)×ft,p​(M−r) 。

考虑一个直观的 O(NM2)O(NM^2)O(NM2) 的动态规划做法,记 dpi,j,kdp_{i,j,k}dpi,j,k​ 表示前 iii 行连通,并且第 iii 行留下的区间为 [j,k][j,k][j,k] 的概率,记 prei,x=∑j=1x∑k=jxdpi,j,k,sufi,x=∑j=xM∑k=jMdpi,j,kpre_{i,x}=\sum_{j=1}^{x}\sum_{k=j}^{x}dp_{i,j,k},suf_{i,x}=\sum_{j=x}^{M}\sum_{k=j}^{M}dp_{i,j,k}prei,x​=∑j=1x​∑k=jx​dpi,j,k​,sufi,x​=∑j=xM​∑k=jM​dpi,j,k​ ,显然有转移
dpi,j,k=ft,p(j−1)×ft,p(M−k)×(prei−1,M−prei−1,j−1−sufi−1,k+1)dp_{i,j,k}=f_{t,p}(j-1)\times f_{t,p}(M-k)\times (pre_{i-1,M}-pre_{i-1,j-1}-suf_{i-1,k+1})dpi,j,k​=ft,p​(j−1)×ft,p​(M−k)×(prei−1,M​−prei−1,j−1​−sufi−1,k+1​)

我们发现转移时并不需要知道 dpi,j,kdp_{i,j,k}dpi,j,k​ ,只需要知道 prei,xpre_{i,x}prei,x​ 和 sufi,xsuf_{i,x}sufi,x​ 即可。

考虑直接利用 prei,xpre_{i,x}prei,x​ 和 sufi,xsuf_{i,x}sufi,x​ 进行动态规划,以计算 prei,xpre_{i,x}prei,x​ 为例,记 prei,x′=∑j=1xdpi,j,xpre'_{i,x}=\sum_{j=1}^{x}dp_{i,j,x}prei,x′​=∑j=1x​dpi,j,x​ ,显然 prei,xpre_{i,x}prei,x​ 就是 prei,x′pre'_{i,x}prei,x′​ 的前缀和。那么根据上面的计算式,有
prei,k′=∑j=1kft,p(j−1)×ft,p(M−k)×(prei−1,M−prei−1,j−1−sufi−1,k+1)pre'_{i,k}=\sum_{j=1}^{k}f_{t,p}(j-1)\times f_{t,p}(M-k)\times (pre_{i-1,M}-pre_{i-1,j-1}-suf_{i-1,k+1})prei,k′​=j=1∑k​ft,p​(j−1)×ft,p​(M−k)×(prei−1,M​−prei−1,j−1​−sufi−1,k+1​)

可以发现,若将括号拆开,每一项都可以表示为只和 i,ki,ki,k 相关的一个量乘以只和 i,ji,ji,j 相关的一个量,可以通过部分和进行优化。

时间复杂度为 O(NM+K)O(NM+K)O(NM+K) 。

#include<bits/stdc++.h>
using namespace std;
const int MAXM = 2e5 + 5;
const int MAXN = 3005;
const int P = 1e9 + 7;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {x = 0; int f = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';x *= f;
}
template <typename T> void write(T x) {if (x < 0) x = -x, putchar('-');if (x > 9) write(x / 10);putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {write(x);puts("");
}
int pre[2][MAXN], suf[2][MAXN];
int n, m, p, q, t, lp[MAXN], rp[MAXN];
int fac[MAXM], inv[MAXM], powp[MAXM], powq[MAXM];
void update(int &x, int y) {x = (x + y >= P) ? (x + y - P) : (x + y);
}
void getdp() {static int sumlp[MAXN], sumrp[MAXN];for (int i = 1; i <= m; i++) {sumlp[i] = (sumlp[i - 1] + lp[i]) % P;pre[1][i] = 1ll * sumlp[i] * rp[i] % P;update(pre[1][i], pre[1][i - 1]);}for (int i = m; i >= 1; i--) {sumrp[i] = (sumrp[i + 1] + rp[i]) % P;suf[1][i] = 1ll * sumrp[i] * lp[i] % P;update(suf[1][i], suf[1][i + 1]);}for (int i = 2, now = 0, from = 1; i <= n; i++, swap(now, from)) {int tmp = 0;for (int j = 1; j <= m; j++) {pre[now][j] = 1ll * sumlp[j] * pre[from][m] % P;update(tmp, 1ll * pre[from][j - 1] * lp[j] % P);update(pre[now][j], P - tmp);update(pre[now][j], P - 1ll * sumlp[j] * suf[from][j + 1] % P);pre[now][j] = 1ll * pre[now][j] * rp[j] % P;update(pre[now][j], pre[now][j - 1]);}tmp = 0;for (int j = m; j >= 1; j--) {suf[now][j] = 1ll * sumrp[j] * pre[from][m] % P;update(tmp, 1ll * suf[from][j + 1] * rp[j] % P);update(suf[now][j], P - tmp);update(suf[now][j], P - 1ll * sumrp[j] * pre[from][j - 1] % P);suf[now][j] = 1ll * suf[now][j] * lp[j] % P;update(suf[now][j], suf[now][j + 1]);}}
}
int power(int x, int y) {if (y == 0) return 1;int tmp = power(x, y / 2);if (y % 2 == 0) return 1ll * tmp * tmp % P;else return 1ll * tmp * tmp % P * x % P;
}
int getc(int x, int y) {if (y > x) return 0;else return 1ll * fac[x] * inv[y] % P * inv[x - y] % P;
}
int getp(int x) {return 1ll * getc(t, x) * powp[x] % P * powq[t - x] % P;
}
void init(int n) {fac[0] = powp[0] = powq[0] = 1;for (int i = 1; i <= n; i++) {fac[i] = 1ll * fac[i - 1] * i % P;powp[i] = 1ll * powp[i - 1] * p % P;powq[i] = 1ll * powq[i - 1] * q % P;}inv[n] = power(fac[n], P - 2);for (int i = n - 1; i >= 0; i--)inv[i] = inv[i + 1] * (i + 1ll) % P;for (int i = 1; i <= m; i++) {lp[i] = getp(i - 1);rp[i] = getp(m - i);}
}
int main() {read(n), read(m);read(p), read(q);p = 1ll * p * power(q, P - 2) % P;q = (P + 1 - p) % P;read(t), init(t), getdp();printf("%d\n", pre[n & 1][m]);return 0;
}

【集训队作业】IOI 2020 集训队作业 试题泛做 13相关推荐

  1. 论弱逼的自我修养——2014集训队CF试题泛做

    为了增长姿势水平提高思考能力,我决定跟着神犇膜一膜2014的集训队作业: 似乎大多数是CF上的DE题,应该比较有含金量(然而博主是个div2连D都没做上过的**): 感觉不久就会弃坑吧,大家来猜猜窝能 ...

  2. Shoi2017试题泛做

    一口气做完六个省的省选(误) Day1 [Shoi2017]期末考试 枚举最大的天数,然后代价贪心地O(1)计算. 1 #include <cstdio> 2 #include <a ...

  3. 2020年电力电缆试题及答案及电力电缆考试平台

    题库来源:安全生产模拟考试一点通公众号小程序 2020年电力电缆试题及答案及电力电缆考试平台,包含电力电缆试题及答案答案和解析及电力电缆考试平台练习.由安全生产模拟考试一点通公众号结合国家电力电缆考试 ...

  4. IOI 2020落幕,中国队团体成绩第一,美籍华裔选手拿下唯一满分

    边策 贾浩楠 发自 凹非寺  量子位 报道 | 公众号 QbitAI 2020年国际信息学奥赛(IOI 2020)完成了第二日比赛,四名中国队选手皆进入前十,分列3~7名,团队总成绩第一! 罗煜翔:来 ...

  5. html大作业_杜绝家长作业 关键是如何监督

    风评 杜绝家长作业 关键是如何监督 中国青年报 ( 2020年11月19日 04 版) 晨雾 / 转帖 张学炬 近日,武汉市教育局发出<关于开展杜绝"家长作业"进一步减轻中小 ...

  6. HTML期末作业课程设计大作业~环境保护学生网页设计作业源码(HTML+CSS)

    HTML期末大作业~基于HTML+CSS环境保护学生网页设计 临近期末, 你还在为HTML网页设计结课作业,老师的作业要求感到头大?HTML网页作业无从下手?网页要求的总数量太多?没有合适的模板?等等 ...

  7. 外部仓库_仓库主要作业流程和WMS作业优化方案

    仓库在人们眼中一直是低效.高成本的地方,即便是增加人手也很难做好仓库管理.那么我们应该采取哪些方式来管理,才能做到降本增效呢? 首先我们需要先了解仓库主要作业流程及在作业中存在的痛点. 1 入库流程 ...

  8. python实验报告代写_TensorFlow作业代写、代做Python程序语言作业、代写github课程作业、Python实验作业代写...

    TensorFlow作业代写.代做Python程序语言作业.代写github课程作业.Python实验作业代写 日期:2019-07-10 10:34 Python Practical Examine ...

  9. 代写php代码作业,代写phpmyadmin留学生作业、代做SQL语言作业、SQL程序设计作业调试、代做PHP script作业...

    代写phpmyadmin留学生作业.代做SQL语言作业.SQL程序设计作业调试.代做PHP script作业 日期:2019-04-20 01:17 Overview:In this assignme ...

最新文章

  1. 从Servlet传值给JSP页面
  2. 转载:一致性 hash 算法( consistent hashing )
  3. 【lucene】lucene自定义 filter
  4. 拥抱开放,Serverless 时代的下一征程
  5. 计算机系统的输入与输出接口是,计算机输入输出系统与接口技术
  6. Eclipse如何重置窗口
  7. java并发编程实战
  8. 阿里云天池大赛——机器学习篇赛题解析(赛题一)上
  9. 计算机毕业设计PHP图书馆图书借阅管理系统
  10. Python爬虫实战 | (10) 爬取猫眼电影《海王》影评并存入MySql数据库
  11. int 10h中断例程
  12. Github上传代码到main分支
  13. Echarts 关系图谱示例
  14. 源代码(二)_尚学堂学习
  15. Unity方便查看日志的插件Reporter
  16. 1-3 Python基本数据类型
  17. Bable详解和前端模块化
  18. 一行代码深度定制你的专属二维码:(amzqr、MyQR制作动态二维码)
  19. Linux命令行模式启动VMware时vmnet-dhcpd和vmnet-natd进程无法启动 VMware进程不全 解决办法
  20. [Linux 学习] grub 引导文件

热门文章

  1. 【长难句分析精讲】并列结构
  2. 美颜sdk与人脸识别技术的结合:为智能化时代注入美感
  3. 黑*头条_第8章_爬虫系统搭建
  4. 淘宝短视频,为什么搬运的短视频没有流量?从算法角度分析
  5. 智慧养老模式和智慧养老系统
  6. 【设计模式】Builder模式
  7. 比较好的文档翻译软件-哪个翻译软件最精准
  8. SDL版仙剑奇侠传读后感
  9. 2022双十一买什么好?行家推荐四大最值得入手的数码好物
  10. RAID的概念和RAID对于SQL性能的影响