【集訓隊作業】IOI 2020 集訓隊作業 試題泛做 13

Codeforces 679E Bear and Bad Powers of 42

不難發現數列中的元素不可能達到很大,我們只需要考慮 4242 的前若干個冪。

考慮沒有賦值操作的做法,則可用線段樹維護區間中最接近 4242 的下一個冪的數與這個冪的差值,在區間加時,只需要在線段樹上 DFS 找到差值變負的位置更新其與 4242 下一個冪的差值即可。

由於一個數導致進行 DFS 的次數是 O(Log42V)O(Log_{42}V) 的,因此這個算法的複雜度是有保證的。

存在賦值操作時,我們會將區間中整段的元素賦爲同一個數。

注意到區間加操作至多破開兩個整段,其餘整段仍然可以被看做是整體,只需要再額外處理一下滿足所有數都相同的區間即可保證複雜度正確。

時間複雜度 O((N+Q)LogN×Log42V)O((N+Q)LogN\times Log_{42}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

二分答案,考慮如何判斷。

注意到不等式 xyz|x-y|\leq z 等價於 xyz,yxzx-y\leq z,y-x\leq z ,可以將限制拆分爲 232^3 個不含絕對值的限制。

因此,我們可以得到 x+y+z,x+yz,xy+z,xyzx+y+z,x+y-z,x-y+z,x-y-z 的取值範圍。

首先,對於確定的 x+y+z,x+yz,xy+z,xyzx+y+z,x+y-z,x-y+z,x-y-z ,只需要它們奇偶性相同便可以得到一組整數解,可以先枚舉這個奇偶性。

2z=(x+y+z)(x+yz)=(xy+z)(xyz)2z=(x+y+z)-(x+y-z)=(x-y+z)-(x-y-z) ,我們可以得到 zz 的取值範圍,若爲空,則顯然無解,否則,取任意一個 zz 的值代入計算即可得到一組合法解。

時間複雜度 O(NLogV)O(\sum 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 軸上,且關於原點對稱。那麼,對於每一條邊所在的直線 ll ,若 ll 與 X 軸交於正半軸,則更靠近正半軸的點,否則,其更靠近負半軸的點,由多邊形的凸性,更靠近兩點之一的邊是一個區間。

那麼,只需要用雙指針計算出對於每個左端點 ll ,最大的 rr 使得邊集 [l,r][l,r] 可以被覆蓋,即可判斷。

在計算半平面交時採用合併有序表排序,時間複雜度 O(N2LogV)O(N^2LogV)

以下代碼沒有使用合併有序表排序,時間複雜度 O(N2LogNLogV)O(N^2LogNLogV)

#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)

#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

不計複雜度,考慮如下做法:

枚舉 xx ,令 s1s_1 爲第 xx 個字符,每次找到 sis_i 的下一個出現位置 sis_i' ,令 si+1s_{i+1} 爲恰好包含 si,sis_i,s_i' 的字符串,直到不存在 sis_i' ,對所達到的 ii 取最大值輸出。

這個做法是正確的,因爲對於任意的 si+1s_{i+1} ,若 si,sis_i,s_{i}' 不分別是 si+1s_{i+1} 的前、後綴,則可以將 si+1s_{i+1} 縮短,從而可以認爲 sis_isi+1s_{i+1} 的一個 border ,上面枚舉的 xx 相當於枚舉了最終串的開頭位置。

考慮用後綴數組優化這個做法,由字符串週期理論,字符串的 border 是由至多 O(LogN)O(LogN) 段等差數列構成的,也就是說,我們只要能夠較快地處理每一段等差數列即可。

那麼,我們只需要支持找到 sis_i' ,計算 sis_i 開頭的後綴和 sis_i' 開頭的後綴的 LCP 即可,可以分別用後綴數組和可持久化線段樹解決。

時間複雜度 O(NLog2N)O(NLog^2N)

#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>t 的情況,可以翻轉序列,下令 s<ts<t

代價中 xixj|x_i-x_j| 的部分可以計入 ai,bi,ci,dia_i,b_i,c_i,d_i ,從而不考慮這部分代價。

考慮從左向右 DP , ii 號點左側路徑的輪廓線是由至多一個路徑開頭、一個路徑結尾和若干條子路徑組成的,路徑開頭在 i>si>s 時出現,路徑結尾在 i<ti<t 時出現。

因此,在狀態中計入子路徑的條數,即可 O(1)O(1) 轉移。

時間複雜度 O(N2)O(N^2)

#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=2 的限制看做圖中的一條邊,則圖中只存在路徑和環。

分別在路徑和環上 DP 即可。

時間複雜度 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

首先,定義 tt 次成功率爲 pp 的操作恰好成功 xx 次的概率爲 ft,p(x)f_{t,p}(x) ,有 ft,p(x)=px(1p)tx(tx)f_{t,p}(x)=p^x(1-p)^{t-x}\binom{t}{x}

對於任意一行,留下的部分是區間 [l,r][l,r] 的概率爲 ft,p(l1)×ft,p(Mr)f_{t,p}(l-1)\times f_{t,p}(M-r)

考慮一個直觀的 O(NM2)O(NM^2) 的動態規劃做法,記 dpi,j,kdp_{i,j,k} 表示前 ii 行連通,並且第 ii 行留下的區間爲 [j,k][j,k] 的概率,記 prei,x=j=1xk=jxdpi,j,k,sufi,x=j=xMk=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} ,顯然有轉移
dpi,j,k=ft,p(j1)×ft,p(Mk)×(prei1,Mprei1,j1sufi1,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,kdp_{i,j,k} ,只需要知道 prei,xpre_{i,x}sufi,xsuf_{i,x} 即可。

考慮直接利用 prei,xpre_{i,x}sufi,xsuf_{i,x} 進行動態規劃,以計算 prei,xpre_{i,x} 爲例,記 prei,x=j=1xdpi,j,xpre'_{i,x}=\sum_{j=1}^{x}dp_{i,j,x} ,顯然 prei,xpre_{i,x} 就是 prei,xpre'_{i,x} 的前綴和。那麼根據上面的計算式,有
prei,k=j=1kft,p(j1)×ft,p(Mk)×(prei1,Mprei1,j1sufi1,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})

可以發現,若將括號拆開,每一項都可以表示爲只和 i,ki,k 相關的一個量乘以只和 i,ji,j 相關的一個量,可以通過部分和進行優化。

時間複雜度爲 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)

本題中,涉及除法的地方不多,分子和分母的乘積始終在六十四位整型範圍內,可以通過手寫有理數類來避免實數運算。

#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

設立超級源點 ssss ,超級匯點 stst 。計算各個點出入度的差 did_i ,若爲正則令 ssss 連向 ii ,若爲負則令 ii 連向 stst ,費用爲 00 ,容量爲 di|d_i|

我們的目標是超級源到超級匯滿流。

對於原圖中的每一條邊:

(1)(1) 、若 fcf\leq c
則將 ff 改小的代價是 11 ,上限爲 ff
ff 改大的代價是 11 ,上限爲 cfc-f
ff 進一步改大的代價是 22 ,上限爲 ++\infty

(2)(2) 、若 f>cf>c
則必然存在的花費爲 fcf-c
ff 改小的代價是 00 ,上限爲 fcf-c
ff 進一步改小的代價是 11 ,上限爲 cc
ff 改大的代價是 22 ,上限爲 ++\infty

再增設一條匯點到源點的邊,容量無窮,費用爲零。

用最小費用最大流計算答案即可。

時間複雜度 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

首先,定義 tt 次成功率爲 pp 的操作恰好成功 xx 次的概率爲 ft,p(x)f_{t,p}(x) ,有 ft,p(x)=px(1p)tx(tx)f_{t,p}(x)=p^x(1-p)^{t-x}\binom{t}{x}

對於任意一行,留下的部分是區間 [l,r][l,r] 的概率爲 ft,p(l1)×ft,p(Mr)f_{t,p}(l-1)\times f_{t,p}(M-r)

考慮一個直觀的 O(NM2)O(NM^2) 的動態規劃做法,記 dpi,j,kdp_{i,j,k} 表示前 ii 行連通,並且第 ii 行留下的區間爲 [j,k][j,k] 的概率,記 prei,x=j=1xk=jxdpi,j,k,sufi,x=j=xMk=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} ,顯然有轉移
dpi,j,k=ft,p(j1)×ft,p(Mk)×(prei1,Mprei1,j1sufi1,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,kdp_{i,j,k} ,只需要知道 prei,xpre_{i,x}sufi,xsuf_{i,x} 即可。

考慮直接利用 prei,xpre_{i,x}sufi,xsuf_{i,x} 進行動態規劃,以計算 prei,xpre_{i,x} 爲例,記 prei,x=j=1xdpi,j,xpre'_{i,x}=\sum_{j=1}^{x}dp_{i,j,x} ,顯然 prei,xpre_{i,x} 就是 prei,xpre'_{i,x} 的前綴和。那麼根據上面的計算式,有
prei,k=j=1kft,p(j1)×ft,p(Mk)×(prei1,Mprei1,j1sufi1,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})

可以發現,若將括號拆開,每一項都可以表示爲只和 i,ki,k 相關的一個量乘以只和 i,ji,j 相關的一個量,可以通過部分和進行優化。

時間複雜度爲 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;
}
發佈了797 篇原創文章 · 獲贊 87 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章