luogu NOIp熱身賽(2018-11-07)題解
T1:區間方差
題目大意:詢問區間方差,支援單點修改
首先把方差的式子展開
得到$d = \frac{a_1 + ... a_n}{n} - \frac{a_1^2 + .. + a_n^2 }{n^2}$
那麼,只需維護$\sum a_i$和$\sum a_i^2$即可
(沒有區間加真是良心)
複雜度$O(n \log n)$
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> namespace1remoon { #define ri register int #define tpr template <typename ra> #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++) #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --) #define gc getchar inline int read() { int p = 0, w = 1; char c = gc();while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); } while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc(); return p * w; } } using namespace std; using namespace remoon; namespace mod_zone { const int mod = 1e9 + 7; inline void inc(int&a, int b) { a += b; if(a >= mod) a -= mod; } inline void dec(int &a, int b) { a -= b; if(a < 0) a += mod; } inline int Inc(int a, int b) { return (a + b >= mod) ? a + b - mod : a + b; } inline int Dec(int a, int b) { return (a - b < 0) ? a - b + mod : a - b; } inline int mul(int a, int b) { return 1ll * a * b % mod; } inline int fp(int a, int k) { int ret = 1; for( ; k; k >>= 1, a = mul(a, a)) if(k & 1) ret = mul(ret, a); return ret; } } using namespace mod_zone; const int sid = 500050; int n, m; int w[sid], s[sid], s2[sid]; #define ls (o << 1) #define rs (o << 1 | 1) inline void upd(int o) { s[o] = Inc(s[ls], s[rs]); s2[o] = Inc(s2[ls], s2[rs]); } inline void build(int o, int l, int r) { if(l == r) { s[o] = w[l]; s2[o] = mul(w[l], w[l]); return; } int mid = (l+ r) >> 1; build(ls, l, mid); build(rs, mid + 1, r); upd(o); } inline void mdf(int o, int l, int r, int p, int v) { if(l == r) { s[o] = v; s2[o] = mul(v, v); return; } int mid = (l + r) >> 1; if(p <= mid) mdf(ls, l, mid, p, v); else mdf(rs, mid + 1, r, p, v); upd(o); } inline int qrys(int o, int l, int r, int ml, int mr) { if(ml > r || mr < l) return 0; if(ml <= l && mr >= r) return s[o]; int mid = (l + r) >> 1; return Inc(qrys(ls, l, mid, ml, mr), qrys(rs, mid + 1, r, ml, mr)); } inline int qryS(int o, int l, int r, int ml, int mr) { if(ml > r || mr < l) return 0; if(ml <= l && mr >= r) return s2[o]; int mid = (l + r) >> 1; return Inc(qryS(ls, l, mid, ml, mr), qryS(rs, mid + 1, r, ml, mr)); } int main() { n = read(); m = read(); rep(i, 1, n) w[i] = read(); build(1, 1, n); rep(i, 1, m) { int c = read(), a = read(), b = read(); if(c == 1) mdf(1, 1, n, a, b); else { int S = qrys(1, 1, n, a, b); int S2 = qryS(1, 1, n, a, b); int iv = fp(b - a + 1, mod - 2); int ans = Dec(mul(b - a + 1, S2), mul(S, S)); printf("%d\n", mul(ans, mul(iv, iv))); } } return 0; }
T2:攀爬者
題目大意:在三維平面上有$n$個點,兩兩高度不相同,一個人會按高度從小到大的順序爬這些點,詢問路徑的總長
兩個點之間的距離為歐幾里得距離
都從小到大爬了,順序固定了就直接模擬吧
複雜度$O(n \log n)$
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> namespace remoon { #define de double #define le long double #define ri register int #define tpr template <typename ra> #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++) #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --) #define gc getchar inline int read() { int p = 0, w = 1; char c = gc(); while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); } while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc(); return p * w; } } using namespace std; using namespace remoon; const int sid = 50050; int n; struct ser { int x, y, z; friend bool operator < (ser a, ser b) { return a.z < b.z; } } t[sid]; int main() { n = read(); rep(i, 1, n) { t[i].x = read(); t[i].y = read(); t[i].z = read(); } sort(t + 1, t + n + 1); de ans = 0; rep(i, 1, n - 1) { de dx = t[i + 1].x - t[i].x; de dy = t[i + 1].y - t[i].y; de dz = t[i + 1].z - t[i].z; ans += sqrt(dx * dx + dy * dy + dz * dz); } printf("%.3lf\n", ans); return 0; }2
T3:蜈蚣
題目大意:定義一段區間的權值為異或和,把一個長為$n$的序列劃分成$m$段,使得各個段的權值和最大
考慮令$f[i][j]$表示到了第$i$個點,已經劃分了$j$段的最大權值
然後列舉$f[k][j - 1]$轉移即可
複雜度$O(n^2 m)$
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> namespace remoon { #define ri register int #define ll long long #define tpr template <typename ra> #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++) #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --) #define gc getchar inline int read() { int p = 0, w = 1; char c = gc(); while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); } while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc(); return p * w; } tpr inline void cmax(ra &a, ra b) { if(a < b) a = b; } } using namespace std; using namespace remoon; int n, m; int w[1005], f[1005][105]; int main() { n = read(); m = read(); rep(i, 1, n) w[i] = read(); memset(f, 128, sizeof(f)); f[0][0] = 0; rep(i, 1, n) { int val = w[i]; drep(j, i - 1, 0) { rep(k, 1, m) cmax(f[i][k], f[j][k - 1] + val); val ^= w[j]; } } printf("%d\n", f[n][m]); return 0; }3
T4:漂浮的鴨子
題目大意:在一張$n$個點和$n$條邊的有向圖中找到權值最大的環
從一個點開始搜尋,並把沿途的點都標記,如果$dfs$到了標記過的點,那麼說明是環,統計方案
注意,標記應該具有同時性
即被從點$i$開始的搜尋標記的點和被從點$j$開始的搜尋標記的點是不同的
複雜度$O(n)$
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> namespace remoon { #define ri register int #define ll long long #define tpr template <typename ra> #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++) #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --) #define gc getchar inline int read() { int p = 0, w = 1; char c = gc(); while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); } while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc(); return p * w; } tpr inline void cmin(ra &a, ra b) { if(a > b) a = b; } tpr inline void cmax(ra &a, ra b) { if(a < b) a = b; } } using namespace std; using namespace remoon; const int sid = 500050; int n, cnp, tim; int cap[sid], vis[sid], nxt[sid], node[sid], val[sid]; ll dis[sid], ans; inline void addedge(int u, int v, int w) { nxt[++ cnp] = cap[u]; cap[u] = cnp; node[cnp] = v; val[cnp] = w; } #define cur node[i] inline void dfs(int o) { vis[o] = tim; for(int i = cap[o]; i; i = nxt[i]) if(!vis[cur]) dis[cur] = dis[o] + val[i], dfs(cur); else if(vis[cur] == tim) cmax(ans, dis[o] - dis[cur] + val[i]); } int main() { n = read(); rep(i, 1, n) { int nxt = read(), w = read(); addedge(i, nxt, w); } rep(i, 1, n) if(!vis[i]) ++ tim, dfs(i); printf("%lld\n", ans); return 0; }4
T5:最大差值
題目大意:求滿足$i < j$的最大的$A_j - A_i$
對每個$j$分別統計,那麼只要維護一個字首最小值即可
複雜度$O(n)$
最大差值#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> namespace remoon { #define ri register int #define ll long long #define tpr template <typename ra> #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++) #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --) #define gc getchar inline int read() { int p = 0, w = 1; char c = gc(); while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); } while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc(); return p * w; } tpr inline void cmin(ra &a, ra b) { if(a > b) a = b; } tpr inline void cmax(ra &a, ra b) { if(a < b) a = b; } } using namespace std; using namespace remoon; int main() { int n = read(); ll ans = -5e9, mi = 5e9; rep(i, 1, n) { ll v = read(); cmin(mi, v); cmax(ans, v - mi); } printf("%lld\n", ans); }5
T6:隨機數生成器
題目大意:一個數$x$,等概率地變成$[1, x]$中的一個數,詢問期望變幾次變成$1$
首先寫出遞推式
$f[n] = \frac{1}{n}(\sum\limits_{i = 1}^{n} f[i]) + 1$
化簡
$f[n] = \frac{n + \sum\limits_{i = 1}^{n - 1} f[i]}{n - 1}$
我們考慮用$S[n] = \sum\limits_{i = 1}^n f[i]$來求解$f[i]$,得到
$(n - 1)S[n] = nS[n - 1] + n$
化簡
$\frac{S[n]}{n} = \frac{S[n - 1]}{n - 1} + \frac{1}{n - 1}$
令$g[n] = \frac{S[n]}{n}$
那麼得到$g[n] = \sum\limits_{i = 1}^{n - 1} \frac{1}{i} = H_{n - 1}$
即$S[n] = n * H_{n - 1}$
即$f[n] = H_{n - 2} + \frac{n}{n - 1}(n \geq 3)$
注意對$n = 2$特判
我們只需要一個能快速求調和級數的方法
套用尤拉公式即可
複雜度$O(1)$...
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> namespace remoon { #define de double #define le long double #define ri register int #define tpr template <typename ra> #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++) #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --) #define gc getchar inline int read() { int p = 0, w = 1; char c = gc(); while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); } while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc(); return p * w; } } using namespace std; using namespace remoon; const double r = 0.57721566490153286060651209008240243104215933593992; de H(int x) { double res = (log(x * 1.0) + log(x + 1.0)) / 2.0; res += 1.0 / (6.0 * x * (x + 1)); res -= 1.0 / (30.0 * x * x * (x + 1) * (x + 1)); return res + r; } de solve(int v) { de ans = 0; if(v == 1) return 0; if(v == 2) return 1; if(v <= 2e7) { rep(i, 1, v - 2) ans += 1.0 / i; ans += (de)(v) / (de)(v - 1); } else ans += H(v - 2) + (de)(v) / (de)(v - 1); return ans; } int main() { int n; cin >> n; printf("%.5lf\n", solve(n)); return 0; }6
T7:大迴圈
題目大意:
求下面這個程式碼的值
int ans = 0; for(a[1] = 1; a[1] <= n; a[1] ++) for(a[2] = 1; a[2] < a[1]; a[2] ++) ....... for(a[k] = 1; a[k] < a[k - 1]; a[k] ++) ans += f(q);
其中,$f(q)$是一個需要計算的常數
如果迴圈了$S$次,那麼答案為$S * f(q)$
迴圈的次數等價於長為$n$的序列,序列元素在$[1, m]$中,滿足嚴格遞增的方案數
從$m$中元素中選出$n$個出來,它們的序唯一,因此$S = \binom{n}{m}$
然後$O(n)$地計算即可
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> namespace remoon { #define ri register int #define ll long long #define tpr template <typename ra> #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++) #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --) #define gc getchar inline ll read() { ll p = 0, w = 1; char c = gc(); while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); } while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc(); return p * w; } } using namespace std; using namespace remoon; namespace mod_zone { const int mod = 1e9 + 7; inline void inc(int &a, int b) { a += b; if(a >= mod) a -= mod; } inline void dec(int &a, int b) { a -= b; if(a < 0) a += mod; } inline int Inc(int a, int b) { return (a + b >= mod) ? a + b - mod : a + b; } inline int Dec(int a, int b) { return (a - b < 0) ? a - b + mod : a - b; } inline int mul(int a, int b) { return 1ll * a * b % mod; } inline int fp(int a, int k) { int ret = 1; for( ; k; k >>= 1, a = mul(a, a)) if(k & 1) ret = mul(ret, a); return ret; } } using namespace mod_zone; const int sid = 500050; ll q; int n, m, k, ans; inline int C(int n, int m) { int jc1 = 1, jc2 = 1, jc3 = 1; rep(i, 1, n) jc1 = mul(jc1, i); rep(i, 1, m) jc2 = mul(jc2, i); rep(i, 1, n - m) jc3 = mul(jc3, i); jc2 = fp(jc2, mod - 2); jc3 = fp(jc3, mod - 2); return mul(jc1, mul(jc2, jc3)); } int main() { n = read(); m = read(); k = read(); q = read() % mod; int x = 1; rep(i, 0, m) { int v = read(); inc(ans, mul(v, x)); x = mul(x, q); } printf("%d\n", mul(ans, C(n, k))); return 0; }7