CGAK 都是好题,尤其喜欢 C
The 3rd Universal Cup. Stage 22: Zhengzhou - Dashboard - Contest - QOJ.ac https://codeforces.com/gym/543949/ https://codeforces.com/gym/105632
题目分区:LBFM / C / GK / AJDIEH 牌线:4 题快 / 5 题快 / 6 题快
给定三个正整数p A , p B , p C p_{A},p_{B},p_{C} p A , p B , p C ,构造三个无穷长 01 串A , B , C A,B,C A , B , C ,满足周期分别为p A , p B , p C p_{A},p_{B},p_{C} p A , p B , p C ,且A ⊕ B = C A\oplus B=C A ⊕ B = C 。1 0 6 10^{6} 1 0 6 。
拿到这题的第一反应是设p A = d p q , p B = d p r , p C = d q r p_{A} = dpq,\ p_{B} = dpr,\ p_{C} = dqr p A = d p q , p B = d p r , p C = d q r ,然后发现样例 2 并不能设为这种形式,就猜测不能表示便无解。(附:正确的设法是p A = d p q a , p B = d p r b , p C = d q r c p_{A} = dpqa,\ p_{B} = dprb,\ p_{C} = dqrc p A = d p q a , p B = d p r b , p C = d q r c )
事实上,有解的必要条件是
{ p C ∣ lcm ( p A , p B ) p A ∣ lcm ( p B , p C ) p B ∣ lcm ( p C , p A ) ⟺ p A = d p q , p B = d p r , p C = d q r \begin{cases} p_{C} \mid \operatorname{lcm}(p_{A},p_{B}) \\ p_{A} \mid \operatorname{lcm}(p_{B},p_{C}) \\ p_{B} \mid \operatorname{lcm}(p_{C},p_{A}) \\ \end{cases} \iff p_{A} = dpq,\ p_{B} = dpr,\ p_{C} = dqr ⎩ ⎪ ⎪ ⎨ ⎪ ⎪ ⎧ p C ∣ l c m ( p A , p B ) p A ∣ l c m ( p B , p C ) p B ∣ l c m ( p C , p A ) ⟺ p A = d p q , p B = d p r , p C = d q r
简要证明如下:考虑S = A ⊕ B S = A\oplus B S = A ⊕ B ,它的一个周期是p S = lcm ( p A , p B ) p_{S} = \operatorname{lcm}(p_{A},p_{B}) p S = l c m ( p A , p B ) 。题目要求C = S C=S C = S 。也就是说已知一个串以p S p_{S} p S 作为周期,那么最小周期p C p_{C} p C 需要满足什么条件?显然是p C ∣ p S p_{C} \mid p_{S} p C ∣ p S ,即p C ∣ lcm ( p A , p B ) p_{C} \mid \operatorname{lcm}(p_{A},p_{B}) p C ∣ l c m ( p A , p B ) 。
至于是否充分,我们只要能构造出来就是充分的。
首先判掉一些平凡的情形。
以下不妨设d = gcd ( p A , p B , p C ) = 1 d = \gcd(p_{A},p_{B},p_{C}) = 1 d = g cd( p A , p B , p C ) = 1 ,否则构造之后在相邻两个数之间插d − 1 d-1 d − 1 个 0。
如果存在恰好两个相等,不妨p A = p B p_{A}=p_{B} p A = p B ,因为已经设d = gcd ( p A , p B , p C ) = 1 d = \gcd(p_{A},p_{B},p_{C}) = 1 d = g cd( p A , p B , p C ) = 1 ,所以p C = 1 p_{C}=1 p C = 1 。例如 (4, 4, 1) 构造 1000, 0111, 1。
剩下就是互不相同,设p A = p q , p B = p r , p C = q r p_{A} = pq,\ p_{B} = pr,\ p_{C} = qr p A = p q , p B = p r , p C = q r ,其中p ≠ q ≠ r p \neq q \neq r p = q = r 。分别构造周期为p , q , r p,q,r p , q , r 的串,两两异或起来就好了。比如 (6, 10, 15),先构造周期为 2,3,5 的 10, 100, 10000,异或之后得到1 0 ′ 1 0 ′ 10 ⊕ 10 0 ′ 100 = 001110 10'10'10\oplus 100'100=001110 1 0 ′ 1 0 ′ 1 0 ⊕ 1 0 0 ′ 1 0 0 = 0 0 1 1 1 0 ,1 0 ′ 1 0 ′ 1 0 ′ 1 0 ′ 10 ⊕ 1000 0 ′ 10000 10'10'10'10'10\oplus 10000'10000 1 0 ′ 1 0 ′ 1 0 ′ 1 0 ′ 1 0 ⊕ 1 0 0 0 0 ′ 1 0 0 0 0 ,10 0 ′ 10 0 ′ 10 0 ′ 10 0 ′ 100 ⊕ 1000 0 ′ 1000 0 ′ 10000 100'100'100'100'100\oplus 10000'10000'10000 1 0 0 ′ 1 0 0 ′ 1 0 0 ′ 1 0 0 ′ 1 0 0 ⊕ 1 0 0 0 0 ′ 1 0 0 0 0 ′ 1 0 0 0 0 。
QED.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 #include <bits/stdc++.h> using namespace std;using ll = long long ;using ull = unsigned long long ;using ulll = unsigned __int128;int main () { int T; cin >> T; while (T--) { int pA, pB, pC; cin >> pA >> pB >> pC; if (lcm (pA, pB) % pC || lcm (pA, pC) % pB || lcm (pB, pC) % pA) { cout << "NO" << endl; } else if (pA == pB && pB == pC) { if (pA == 2 ) { cout << "NO" << endl; } else if (pA == 1 ) { cout << "YES" << endl; cout << 1 << endl; cout << 1 << endl; cout << 0 << endl; } else { cout << "YES" << endl; cout << "10" << string (pA - 2 , '0' ) << endl; cout << "01" << string (pA - 2 , '0' ) << endl; cout << "11" << string (pA - 2 , '0' ) << endl; } } else { int d = gcd (gcd (pA, pB), pC); string a, b, c; pA /= d; pB /= d; pC /= d; if (pA == 1 || pB == 1 || pC == 1 ) { if (pA == 1 ) { a = "1" ; b = "1" + string (pB - 1 , '0' ); c = "0" + string (pC - 1 , '1' ); } else if (pB == 1 ) { a = "0" + string (pA - 1 , '1' ); b = "1" ; c = "1" + string (pC - 1 , '0' ); } else { a = "1" + string (pA - 1 , '0' ); b = "0" + string (pB - 1 , '1' ); c = "1" ; } } else { int p = gcd (pA, pB); int q = gcd (pA, pC); int r = gcd (pB, pC); for (int i = 0 ; i < pA; i++) { int x = (i % p == 0 ) ^ (i % q == 0 ); a += x + '0' ; } for (int i = 0 ; i < pB; i++) { int x = (i % p == 0 ) ^ (i % r == 0 ); b += x + '0' ; } for (int i = 0 ; i < pC; i++) { int x = (i % q == 0 ) ^ (i % r == 0 ); c += x + '0' ; } } cout << "YES" << endl; for (auto & x : a) cout << x << string (d - 1 , '0' ); cout << endl; for (auto & x : b) cout << x << string (d - 1 , '0' ); cout << endl; for (auto & x : c) cout << x << string (d - 1 , '0' ); cout << endl; } } return 0 ; }
折一个四面体,实际操作后发现,底面相同时,三个侧面的数字是确定的,因此每个位置只可能走过一次。BFS 即可。时间复杂度O ( n 2 ) \mathcal{O}(n^{2}) O ( n 2 ) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include <bits/stdc++.h> using namespace std;const int dx[2 ][3 ] = { { 0 , 0 , -1 }, { 0 , 0 , 1 }, }; const int dy[2 ][3 ] = { { -1 , 1 , -1 }, { -1 , 1 , 1 }, }; const int to[2 ][5 ][3 ] = {{ {}, { 2 , 4 , 3 }, { 1 , 3 , 4 }, { 4 , 2 , 1 }, { 3 , 1 , 2 } }, { {}, { 4 , 2 , 3 }, { 3 , 1 , 4 }, { 2 , 4 , 1 }, { 1 , 3 , 2 } }}; int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cout.tie (nullptr ); int n; cin >> n; vector mp (n + 2 , vector<int >(2 * n + 3 )) ; for (int i = 1 ; i <= n; i++) { for (int j = 1 ; j <= 2 * i - 1 ; j++) { cin >> mp[i][j]; } } queue<pair<int , int >> Q; Q.push ({ 1 , 1 }); vector dis (n + 2 , vector<int >(2 * n + 3 , -1 )) ; dis[1 ][1 ] = 0 ; while (!Q.empty ()) { auto [x, y] = Q.front (); Q.pop (); bool op = y & 1 ; for (int i = 0 ; i < 3 ; i++) { int nx = x + dx[op][i], ny = y + dy[op][i]; if (mp[nx][ny] == 0 ) continue ; if (dis[nx][ny] != -1 ) continue ; if (mp[nx][ny] == to[op][mp[x][y]][i]) { dis[nx][ny] = dis[x][y] + 1 ; Q.push ({ nx, ny }); } } } int x, y; cin >> x >> y; cout << dis[x][y] << endl; return 0 ; }
正着不好做,但如果倒推,操作就是唯一的。
从目标倒着模拟,得到一组答案,顺便得到了有解条件以及策略的正确性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <bits/stdc++.h> using namespace std;bool check (int x) { assert (x > 0 ); return (x & (x - 1 )) == 0 ; } int main () { int A, B, X, Y; cin >> A >> B >> X >> Y; A = max (1 , A); B = max (1 , B); int d1 = gcd (A, X), d2 = gcd (B, Y); if (!check (A / d1) || !check (B / d2)) { cout << -1 << endl; return 0 ; } stack<array<int , 4>> res; while ((X != A && X != 0 ) || (Y != B && Y != 0 )) { int cx = (X * 2 < A) ? 0 : A; int cy = (Y * 2 < B) ? 0 : B; X = 2 * X - cx; Y = 2 * Y - cy; res.push ({ X, Y, cx, cy }); } cout << res.size () << endl; while (!res.empty ()) { auto [x1, y1, x2, y2] = res.top (); res.pop (); cout << x1 << " " << y1 << " " << x2 << " " << y2 << endl; } return 0 ; }
只有两种情形,一种是第二天某个任务的开始时间和第一天相同,这样从这个任务开始,就会重复前一天的状态,是个循环;另一种是第二天和第一天任务开始时间完全不同,这样任务就会无限期拖延,且每天的拖延时间是相同的。两种都不难计算。
时间复杂度O ( n + q ) \mathcal{O}(n+q) O ( n + q ) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include <bits/stdc++.h> using namespace std;using ll = long long ;int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int n, k, q; cin >> n >> k >> q; vector<ll> a (n) , b (n) ; for (int i = 0 ; i < n; i++) { cin >> a[i] >> b[i]; } ll now = 0 ; vector<ll> a1 (n) , a2 (n) ; for (int i = 0 ; i < n; i++) { now = a1[i] = max (now, a[i]); now += b[i]; } bool flag = 1 ; for (int i = 0 ; i < n; i++) { now = a2[i] = max (now, a[i] + k); now += b[i]; if (a2[i] == a1[i] + k) { flag = 0 ; } } ll delta = a2. back () - a1. back (); while (q--) { int x, i; cin >> x >> i; i--; ll res; if (x == 1 ) { res = a1[i] + b[i] - 1 ; } else { res = a2[i] + b[i] - 1 ; if (flag) { res += (x - 2ll ) * delta; } else { res += (x - 2ll ) * k; } } ll d = res / k, h = res % k; if (h == 0 ) { h = k; d--; } cout << (++d) << " " << h << endl; } return 0 ; }
给定长为N N N 的整数序列A A A 及Q Q Q 个操作:对于所有i ∈ [ L , R ] i \in [L,R] i ∈ [ L , R ] ,更新A i ← A i + v A_i \gets A_i + v A i ← A i + v ;并判断元素A L , A L + 1 , … , A R A_L, A_{L+1}, \ldots, A_R A L , A L + 1 , … , A R 是否能被分成( R − L + 1 ) / 2 (R-L+1)/2 ( R − L + 1 ) / 2 对总和相同的整数对。1 ⩽ N , Q , A i , v ⩽ 2 × 1 0 5 1 \leqslant N,Q,A_{i},v \leqslant 2 \times 10^{5} 1 ⩽ N , Q , A i , v ⩽ 2 × 1 0 5 。
设c c c 是区间平均数,f n f_{n} f n 表示区间内等于n n n 的数的数量,需要满足
f c + n = f c − n for all n ∈ Z f_{c+n} = f_{c-n} \text{for all} n \in \mathbb{Z} f c + n = f c − n for all n ∈ Z
这就很有幂级数的味道了,考虑形式幂级数
F ( z ) = ∑ n = − ∞ + ∞ z n f n F(z) = \sum_{n=-\infty}^{+\infty} z^{n} f_{n} F ( z ) = n = − ∞ ∑ + ∞ z n f n
其中z z z 是形式参量。需要判断
1 z c F ( z ) = z c F ( z − 1 ) \dfrac{1}{z^{c}} F(z) = z^{c} F(z^{-1}) z c 1 F ( z ) = z c F ( z − 1 )
可以随机一个 ULL 哈希值z = x z=x z = x 判定幂级数相等。
具体地,维护区间信息
M + ( z ) = ∑ i = L R z A i , M − ( z ) = ∑ i = L R z − A i , c = 1 R − L + 1 ∑ i = L R A i M_+(z) = \sum_{i=L}^{R} z^{A_i}, \quad M_-(z) = \sum_{i=L}^{R} z^{-A_i}, \quad c = \dfrac{1}{R-L+1} \sum_{i=L}^{R} A_i M + ( z ) = i = L ∑ R z A i , M − ( z ) = i = L ∑ R z − A i , c = R − L + 1 1 i = L ∑ R A i
满足条件当且仅当M + ( z ) = z 2 c M − ( z ) M_+(z) = z^{2 c} M_-(z) M + ( z ) = z 2 c M − ( z ) 。
线段树维护区间乘区间和,复杂度O ( N + Q log N ) \mathcal{O}(N + Q \log N) O ( N + Q log N ) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 struct Lazy { i64 add{ 0 }; Z po{ 1 }; Z ne{ 1 }; void apply (const Lazy& o) & { add += o.add; po *= o.po; ne *= o.ne; } }; struct Info { i64 sum{ 0 }; Z po{ 0 }; Z ne{ 0 }; int len{ 1 }; void apply (const Lazy& o) & { sum += o.add * len; po *= o.po; ne *= o.ne; } }; Info operator + (const Info& L, const Info& R) { return { L.sum + R.sum, L.po + R.po, L.ne + R.ne, L.len + R.len }; } constexpr int maxN = 4e5 ;int main () { cin.tie (nullptr )->sync_with_stdio (false ); Z po = u64 (rnd ()); Z ne = 1 / po; vector<Z> powpo (maxN + 1 ) , powne (maxN + 1 ) ; powpo[0 ] = 1 ; powne[0 ] = 1 ; for (auto i = 0 ; i < maxN; i++) { powpo[i + 1 ] = powpo[i] * po; powne[i + 1 ] = powne[i] * ne; } int N, Q; cin >> N >> Q; vector<Info> a (N) ; for (auto n = 0 ; n < N; n++) { int x; cin >> x; x *= 2 ; a[n] = { x, powpo[x], powne[x], 1 }; } LazySegmentTree<Info, Lazy> seg (a) ; while (Q--) { int op; cin >> op; if (op == 1 ) { int L, R, v; cin >> L >> R >> v; L--; v *= 2 ; seg.update (L, R, { v, powpo[v], powne[v] }); } else { int L, R; cin >> L >> R; L--; auto [sum, pores, neres, _] = seg.query (L, R); if (sum % (R - L) != 0 ) { cout << "NO\n" ; } else { auto c = sum / (R - L); if (pores * qpow (ne, c) == neres * qpow (po, c)) { cout << "YES\n" ; } else { cout << "NO\n" ; } } } } return 0 ; }
游戏有n n n 个关卡,初始在第一关。每一关有p p p 的概率失败,且每一关失败的概率相互独立。如果在某一关失败,需要从第一关重新开始。有k k k 个特殊道具,失败后可以从当前关卡继续挑战(注意:这种情形下当前关卡被视作未通过)。求通关全部n n n 个关卡所需要的最小期望挑战次数。1 ⩽ n ⩽ 1 0 5 1 \leqslant n \leqslant 10^{5} 1 ⩽ n ⩽ 1 0 5 ,0 ⩽ k ⩽ 1 0 9 0 \leqslant k \leqslant 10^{9} 0 ⩽ k ⩽ 1 0 9 ,0 < p ⩽ 0.5 0<p \leqslant 0.5 0 < p ⩽ 0 . 5 ,n p ⩽ 20 np \leqslant 20 n p ⩽ 2 0 。
Notes1:观察观察 1:
如果我们拥有无限道具,并且采取一失败就立刻使用道具的策略,通关时道具的期望使用次数为n p 1 − p \dfrac{np}{1-p} 1 − p n p 注意n p ⩽ 20 np \leqslant 20 n p ⩽ 2 0 的限制,所以当k k k 很大时我们可以认为采取上述策略是最优的 事实上,当k > 165 k > 165 k > 1 6 5 时,答案可以直接近似为n 1 − p \dfrac{n}{1-p} 1 − p n 证明 1.1: 对于每一关,我们通关的期望次数为1 1 − p \dfrac{1}{1-p} 1 − p 1 ,那么道具的期望使用次数就为1 1 − p − 1 = p 1 − p \dfrac{1}{1-p}-1 = \dfrac{p}{1-p} 1 − p 1 − 1 = 1 − p p ,因为有n n n 关,所以期望总次数为n p 1 − p \dfrac{np}{1-p} 1 − p n p 证明 1.3:n p 1 − p + n = n 1 − p \dfrac{np}{1-p} + n = \dfrac{n}{1-p} 1 − p n p + n = 1 − p n 观察 2:
当 “道具数” 较小时,我们会尽可能将道具留到后面的关卡使用 当挑战失败时,我们会决策 是 / 否 使用道具。当 “道具数” 确定时,这一决策是单调的:存在t t t ,在t t t 关之前失败我们会选择从头开始,在t t t 关之后失败我们会选择使用道具 Notes2:不使用道具当k k k 较小时,考虑 DP。设f i , j f_{i,j} f i , j 表示当前有i i i 个道具,已通过j j j 关的情况下,还需要的最小期望挑战次数。
如果当前已经通过第j j j 关, 根据单调性,若在第j j j 关失败时不使用道具,则失败后在到达j + 1 j + 1 j + 1 关之前,都不会再使用道具。
从第j j j 关,不使用道具直到通过第j + 1 j+1 j + 1 关的期望次数为( 1 1 − p ) j + 1 {\bigg(\dfrac{1}{1-p}\bigg)}^{j+1} ( 1 − p 1 ) j + 1 。
以下给出证明:
若第一次挑战时成功通关,那么次数为1 1 1 ,概率为1 − p 1-p 1 − p ;若第一次挑战失败,问题则转化为求从第一关开始,不使用道具一直通到j + 1 j+1 j + 1 关所需要的最小期望挑战次数。
整理一下,现在要求的是 “不使用道具,从第一关开始到第k k k 关的最小期望挑战次数”(其实出题组很贴心,第一个样例就是这个,但赛时因不会求这个遂放弃)
设T k T_{k} T k 为通到第k k k 关的最小期望挑战次数,那么T k − 1 T_{k-1} T k − 1 就为通到k − 1 k - 1 k − 1 关的最小期望挑战次数。假设我们已经通到k − 1 k-1 k − 1 关,那么再挑战一次,有p p p 的概率失败,还需要T k T_{k} T k 的次数,有1 − p 1-p 1 − p 的概率成功,还需要0 0 0 次。得到
T k = T k − 1 + 1 + p T k + ( 1 − p ) 0 T_{k} = T_{k-1}+1+p T_{k}+(1-p) 0 T k = T k − 1 + 1 + p T k + ( 1 − p ) 0
解得
T k = 1 − ( 1 − p ) k p ( 1 − p ) k T_{k} = \dfrac{1-(1-p)^{k}}{p (1-p)^{k}} T k = p ( 1 − p ) k 1 − ( 1 − p ) k
所以从第j j j 关,不使用道具通过j + 1 j+1 j + 1 关的期望挑战次数为
1 + 0 ( 1 − p ) + p T j + 1 = 1 + p 1 − ( 1 − p ) j + 1 p ( 1 − p ) j + 1 = 1 ( 1 − p ) j + 1 1+0 (1-p)+p T_{j+1} = 1+p \dfrac{1-(1-p)^{j+1}}{p (1-p)^{j+1}}=\dfrac{1}{(1-p)^{j+1}} 1 + 0 ( 1 − p ) + p T j + 1 = 1 + p p ( 1 − p ) j + 1 1 − ( 1 − p ) j + 1 = ( 1 − p ) j + 1 1
所以在不使用道具的情况下,
f i , j = ( 1 1 − p ) j + 1 + f i , j + 1 f_{i,j}=\bigg(\dfrac{1}{1-p}\bigg)^{j+1}+f_{i,j+1} f i , j = ( 1 − p 1 ) j + 1 + f i , j + 1
Notes3:使用道具f i , j f_{i,j} f i , j 表示还剩i i i 个道具,已通过j j j 关,还需要的最小期望挑战次数。
那么当我们在这种状态下再挑战一次,有p p p 的概率还需要f i − 1 , j f_{i-1,j} f i − 1 , j 的挑战次数,有1 − p 1-p 1 − p 的概率还需要f i , j + 1 f_{i,j+1} f i , j + 1 的挑战次数,所以
f i , j − 1 = p f i − 1 , j + ( 1 − p ) f i , j + 1 f_{i,j}-1 = p f_{i-1,j} + (1-p) f_{i,j+1} f i , j − 1 = p f i − 1 , j + ( 1 − p ) f i , j + 1
我们也可以设其他状态,比如 “已经使用了i i i 个道具,已经通过j j j 关的情况下, 还需要的最小期望挑战次数 ,其关键在于要将已知量作为一个入度为0 0 0 的节点
版本一:f i , j f_{i,j} f i , j 表示 “还剩i i i 个道具,已通过j j j 关,还需要的最小期望挑战次数”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <bits/stdc++.h> using namespace std;using ll = long long ;#define double long double double qpow (double a, ll b) { double res = 1 ; while (b) { if (b & 1 ) { res *= a; } b >>= 1 ; a = a * a; } return res; } int main () { ios::sync_with_stdio (false ); cin.tie (0 ), cout.tie (0 ); int n, k; cin >> n >> k; double p, q; cin >> p; q = 1 - p; cout << fixed << setprecision (12 ); double ans; if (k > 165 ) { ans = n / (1 - p); cout << ans << endl; return ; } vector<vector<double >> f (170 , vector <double >(n + 1 )); for (int i = 0 ; i <= k; i++) { for (int j = n - 1 ; j >= 0 ; j--) { f[i][j] = qpow (1 / q, j + 1 ) + f[i][j + 1 ]; if (i) f[i][j] = min (1 + p * f[i - 1 ][j] + q * f[i][j + 1 ], f[i][j]); } } ans = f[k][0 ]; cout << ans << endl; return 0 ; }
版本二:f i , j f_{i,j} f i , j 表示 “已使用i i i 个道具,已通过j j j 关,还需要的最小期望挑战次数”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <bits/stdc++.h> using namespace std;using ll = long long ;#define double long double double qpow (double a, ll b) { double res = 1 ; while (b) { if (b & 1 ) { res *= a; } b >>= 1 ; a = a * a; } return res; } int main () { ios::sync_with_stdio (false ); cin.tie (0 ), cout.tie (0 ); int n, k; cin >> n >> k; double p, q; cin >> p; q = 1 - p; cout << fixed << setprecision (12 ); cerr << fixed << setprecision (12 ); double ans; if (k > 165 ) { ans = n / (1 - p); cout << ans << endl; return ; } vector<vector<double >> f (170 , vector <double >(n + 1 )); for (int i = k; i >= 0 ; i--) { for (int j = n - 1 ; j >= 0 ; j--) { f[i][j] = f[i][j + 1 ] + qpow (1 / q, j + 1 ); if (i != k) f[i][j] = min (f[i][j], 1 + (1 - p) * f[i][j + 1 ] + p * f[i + 1 ][j]); } } ans = f[0 ][0 ]; cout << ans << endl; return 0 ; }
周期是异或的 highbit,复杂度O ( 1 ) \mathcal{O}(1) O ( 1 ) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <bits/stdc++.h> using namespace std;using ull = unsigned long long ;int main () { int T; cin >> T; while (T--) { ull L, R; cin >> L >> R; ull x = 1ULL << std::__lg(L ^ R); cout << (L % x) << endl; } return 0 ; }
给定N , K N,K N , K 和序列A = ( A 1 , A 2 , … , A N ) A = (A_{1},A_{2},\dots, A_{N}) A = ( A 1 , A 2 , … , A N ) ,构造一组概率p 1 , p 2 , … , p N p_{1},p_{2},\dots, p_{N} p 1 , p 2 , … , p N 。一个随机抽奖机器每轮会以P i P_{i} P i 的概率选择i i i ,直到选到的集合大小恰好为K K K 时输出。要求概率满足∑ p i = K \sum p_{i} = K ∑ p i = K ,且对于任意K K K 元子集S S S ,抽奖机器输出S S S 的概率 proportional to∏ i ∈ S A i \prod_{i\in S} A_{i} ∏ i ∈ S A i 。K < N ⩽ 1 0 5 K<N \leqslant 10^{5} K < N ⩽ 1 0 5 ,A i ⩽ 1 0 9 A_{i} \leqslant 10^{9} A i ⩽ 1 0 9 。
要求构造p p p 满足
∏ i ∈ S p i ∏ i ∉ S ( 1 − p i ) ∝ ∏ i ∈ S A i \prod\limits_{i\in S} p_{i}\prod\limits_{i\notin S}(1-p_{i}) \propto \prod\limits_{i\in S} A_{i} i ∈ S ∏ p i i ∈ / S ∏ ( 1 − p i ) ∝ i ∈ S ∏ A i
等价于
p i 1 − p i ∝ A i \dfrac{p_{i}}{1 - p_{i}} \propto A_{i} 1 − p i p i ∝ A i
设比例系数为c c c ,得到p i = A i A i + c p_{i}=\dfrac{A_{i}}{A_{i}+c} p i = A i + c A i ,结合∑ p i = K \sum p_{i} = K ∑ p i = K ,就需要
∑ i = 1 N A i A i + c = K . \sum_{i=1}^{N}\frac{A_{i}}{A_{i}+c}=K. i = 1 ∑ N A i + c A i = K .
这个式子是关于c c c 的N N N 次多项式,不能直接求得。而左式关于c c c 是单调的,可以二分c c c 。
复杂度O ( N ( log V + log ϵ − 1 ) ) \mathcal{O}(N (\log V + \log \epsilon^{-1})) O ( N ( log V + log ϵ − 1 ) ) 。
特别注意,浮点数二分在l l l 和r r r 很大时,用r − l > eps r-l>\operatorname{eps} r − l > e p s 是不行的,因为小数部分的精度已经丢失。先给出 TLE 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <bits/stdc++.h> using namespace std;#define double long double constexpr double eps = 1e-18 ;signed main () { cout << fixed << setprecision (12 ); int N, K; cin >> N >> K; vector<double > A (N) ; for (int i = 0 ; i < N; i++) { cin >> A[i]; } auto calc = [&](double c) { vector<double > p (N); for (int i = 0 ; i < N; i++) { p[i] = A[i] / (A[i] + c); } return p; }; double l = 0 , r = 1e15 ; while (r - l > eps) { double mid = (l + r) / 2 ; auto p = calc (mid); if (accumulate (p.begin (), p.end (), 0.0l ) < K) { r = mid; } else { l = mid; } } auto p = calc (l); for (int i = 0 ; i < N; i++) { cout << p[i] << endl; } return 0 ; }
有三种修改方案:
二分一个在[ 0 , 1 ] [0,1] [ 0 , 1 ] 之间的数,对于本题,可以二分p 1 p_{1} p 1 。
判断l l l 和r r r 的相对差:即判断r − l l \dfrac{r-l}{l} l r − l 是否在误差范围内。
1 2 3 4 5 6 7 8 9 10 double l = 0 , r = 1e15 ;while ((r - l) / l > eps) { double mid = (l + r) / 2 ; auto p = calc (mid); if (accumulate (p.begin (), p.end (), 0.0l ) < K) { r = mid; } else { l = mid; } }
控制二分次数:V ⋅ ϵ − 1 = 1 0 15 ⋅ 1 0 18 = 1 0 23 V \cdot \epsilon^{-1}=10^{15}\cdot 10^{18}=10^{23} V ⋅ ϵ − 1 = 1 0 1 5 ⋅ 1 0 1 8 = 1 0 2 3 ,log V + log ϵ − 1 = 76 \log V + \log \epsilon^{-1}=76 log V + log ϵ − 1 = 7 6 ,因此二分100 100 1 0 0 次足够。 1 2 3 4 5 double l = 0 , r = 1e15 ;for (int _ = 0 ; _ < 100 ; _++) { double mid = (l + r) / 2 ; }
这三种都能跑到 100ms 以内。