2021.10.16CSP模擬賽22 賽後總結
A. 區間
這題似乎用 \(ST\) 表,單調棧等各種方法都可以過。
我用的是無腦線段樹(智商不夠,資料結構來湊)。
簡單來說,維護一下區間最小值及其位置即可,然後遞迴輸出。
直接貼程式碼吧。
這個 \(pushup\) 好像不能寫三目運算子(不知道為什麼),大樣例一直過不去,然後調了半天 \(QwQ\)。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #define ls rt << 1 #define rs rt << 1 | 1 using namespace std; const int N = 2e5 + 10; int n; int a[N], mins[N << 2], pos[N << 2]; inline void pushup(int rt){ if(mins[ls] <= mins[rs]) pos[rt] = pos[ls], mins[rt] = mins[ls]; else pos[rt] = pos[rs], mins[rt] = mins[rs]; } inline void build(int l, int r, int rt){ if(l == r){ mins[rt] = a[l]; pos[rt] = l; return; } int mid = (l + r) >> 1; build(l, mid, ls); build(mid + 1, r, rs); pushup(rt); } inline int query(int L, int R, int l, int r, int rt){ if(l > R || r < L) return 0; if(L <= l && r <= R) return pos[rt]; int mid = (l + r) >> 1; int res = 0; if(L <= mid) res = query(L, R, l, mid, ls); if(R > mid){ int id = query(L, R, mid + 1, r, rs); if(a[id] < a[res]) res = id; } return res; } struct node{ int l, r; }ans[N]; int cnt; inline void dfs(int l, int r, int tmp){ if(l > r) return; int id = query(l, r, 1, n, 1); int res = a[id] - tmp, d = 0; while(res > 0){ ans[++cnt].l = l, ans[cnt].r = r; res--, d++; } dfs(l, id - 1, tmp + d), dfs(id + 1, r, tmp + d); } int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); a[0] = 1e9; memset(mins, 0x3f, sizeof(mins)); build(1, n, 1); dfs(1, n, 0); printf("%d\n", cnt); for(int i = 1; i <= cnt; i++) printf("%d %d\n", ans[i].l, ans[i].r); return 0; }
B. 樹
事實上我並不是很會算期望,所以就算給我 \(n \leq 10\) 資料的我也做不出來 \(QwQ\)。
考場上當然就直接棄了。
後來聽學長講,發現是一波大力推式子。
由於輸入的是一棵樹,所以從 \(u\) 到 \(v\) 的路徑是先網上走到 \(lca(u, v)\),再往下走到 \(v\)。
所以先推出來從 \(x\) 節點走到 \(fa_x\) 的期望,然後推從 \(fa_x\) 走到 \(x\) 的期望(懶得打了,可以去看別人的題解)。就可以求解了。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <queue> #define ll long long using namespace std; inline int read(){ int x = 0; char ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x; } const int mod = 1e9 + 7; const int N = 1e5 + 10; int n, m; struct node{ int v, nxt; }edge[N << 1]; int head[N], tot; int ans; int fa[N][20], dep[N]; ll f[N],g[N]; inline void add(int x, int y){ edge[++tot] = (node){y, head[x]}; head[x] = tot; } inline void dfs1(int x, int p){ fa[x][0] = p, dep[x] = dep[p] + 1; for(int i = 1; i <= 20; i++) fa[x][i] = fa[fa[x][i - 1]][i - 1]; f[x] = 1; for(int i = head[x]; i; i = edge[i].nxt){ int y = edge[i].v; if(y == p) continue; dfs1(y, x); f[x] = (f[x] + f[y] + 1) % mod; } } inline void dfs2(int x, int p){ for(int i = head[x]; i; i = edge[i].nxt){ int y = edge[i].v; if(y == p) continue; g[y] = (g[x] + f[x] - f[y]) % mod; dfs2(y, x); } } inline void dfs3(int x, int p){ f[x] += f[p], g[x] += g[p]; for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].v != p) dfs3(edge[i].v, x); } inline int lca(int a, int b){ if(dep[a] < dep[b]) swap(a, b); for(int i = 20; i >= 0; i--) if(dep[fa[a][i]] >= dep[b]) a = fa[a][i]; if(a == b) return a; for(int i = 20; i >= 0; i--) if(fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i]; return fa[a][0]; } signed main(){ n = read(), m = read(); for(int i = 1; i < n; i++){ int u = read(), v = read(); add(u, v), add(v, u); } dfs1(1, 0); f[1]--; dfs2(1, 0), dfs3(1, 0); while(m--){ int u = read(), v = read(); int k = lca(u, v); // cout <<" dd " <<k << endl; printf("%lld\n", ((f[u] - f[k] + g[v] - g[k]) % mod + mod) % mod); } return 0; }
C. 做運動
巨水的一道題,但是高一學生們全都寫的二分 + \(dij\)(默契十足),複雜度 \(O(nlog_n^2)\),然後全都被卡到 10 ~ 30 分不等……
然鵝正解是並查集 + \(dij\),複雜度 \(O(nlogn)\)。
先按溫度排序,然後不停加邊,當起點和終點在同一集合中時,最大溫度就是那條邊的溫度,然後把所有合法的邊全都加進去,跑個最短路即可。
#include <iostream> #include <cstdio> #include <algorithm> #include <queue> #include <cstring> #define INF 1e18 #define ll long long using namespace std; inline ll read(){ ll x = 0; char ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x; } const ll N = 5e5 + 10; const ll M = 1e6 + 10; ll n, m; struct node{ ll u, v, t, c, nxt; bool operator < (const node &b) const{ return t < b.t; } }edge[M << 1], e[M]; ll head[N], tot; ll temp[M]; ll st, en; ll dis[N], f[N]; struct Que{ ll x, dis; bool operator < (const Que &b) const{ return dis > b.dis; } }; inline void add(ll x, ll y, ll c){ edge[++tot].v = y, edge[tot].c = c, edge[tot].nxt = head[x]; head[x] = tot; } inline void dijkstra(){ priority_queue <Que> q; q.push((Que){st, 0}); for(int i = 1; i <= n; i++) dis[i] = INF; dis[st] = 0; while(!q.empty()){ Que now = q.top(); q.pop(); ll x = now.x; if(dis[x] < now.dis) continue; for(ll i = head[x]; i; i = edge[i].nxt){ ll y = edge[i].v; if(dis[y] > dis[x] + edge[i].c){ dis[y] = dis[x] + edge[i].c; q.push((Que){y, dis[y]}); } } } } inline int find(int x){ return f[x] == x ? x : f[x] = find(f[x]); } signed main(){ n = read(), m = read(); for(ll i = 1; i <= m; i++){ e[i].u = read(), e[i].v = read(), e[i].t = read(), e[i].c = read(); } st = read(), en = read(); for(int i = 1; i <= n; i++) f[i] = i; sort(e + 1, e + 1 + m); ll mint, pos; for(int i = 1; i <= m; i++){ int fu = find(e[i].u), fv = find(e[i].v); if(fu != fv) f[fu] = fv; add(e[i].u, e[i].v, e[i].c * e[i].t), add(e[i].v, e[i].u, e[i].c * e[i].t); if(find(st) == find(en)){ mint = e[i].t, pos = i; break; } } for(int i = pos + 1; i <= m; i++){ if(e[i].t != e[pos].t) break; add(e[i].u, e[i].v, e[i].c * e[i].t), add(e[i].v, e[i].u, e[i].c * e[i].t); } dijkstra(); printf("%lld %lld\n", mint, dis[en]); return 0; }
D. 手機訊號
考場上打 \(O(n^2)\) 暴力,結果沒注意寫了個 \(c * c\) 直接 \(1e36\) 爆 \(long\ long\) 了,然後 30pts 都沒有拿到 \(QwQ\)。
正解似乎是 \(set\) + 亂搞。
然鵝還不太會,先咕了,以後會了在補吧。
本文來自部落格園,作者:{xixike},轉載請註明原文連結:https://www.cnblogs.com/xixike/p/15421858.html