比賽-模擬賽 (Aug 18, 2018)
阿新 • • 發佈:2018-08-18
template lin priority pac operator name inline 代碼 put
維護前 \(k\) 大的正解是樹套樹,但是顯然用不上,直接 \(O(k^2)\) 暴力合並兩個區間(法二中是多個子樹,所以兩兩合並即可)即可(暴力維護前k大方法)。
1.) 小X的質數
線性篩就可以了。由唯一分解定理,如果 $ x = p_a \cdot p_b $ ,那麽 \(x\) 也一定只能這樣分解質因數。所以 \(x\) 也是符合題目條件的數。
#include <cstdio> #include <ctype.h> #include <stack> using namespace std; template<typename T> void rd(T &num) { char tt; while (!isdigit(tt = getchar())); num = tt - '0'; while (isdigit(tt = getchar())) num = num * 10 + tt - '0'; return; } template<typename T> void pt(T num) { stack<char> S; do S.push(num % 10 + '0'); while (num /= 10); while (!S.empty()) putchar(S.top()), S.pop(); putchar('\n'); return; } const int _N = 10002000; int P[_N], sum[_N], Pcnt; bool mk[_N], mk2[_N]; int main() { int N = 10001000, T; mk[1] = 1; for (int i = 2; i <= N; ++i) { if (!mk[i]) P[++Pcnt] = i; for (int j = 1; j <= Pcnt && i * P[j] <= N; ++j) { mk[i * P[j]] = 1; if (!mk[i]) mk2[i * P[j]] = 1; if (i % P[j] == 0) break; } sum[i] = sum[i - 1]; if (!mk[i] || mk2[i]) ++sum[i]; } rd(T); while (T--) { int l, r; rd(l), rd(r); pt(sum[r] - sum[l - 1]); } return 0; }
2.) 小X的密室
\(dis_{S, i}\) 表示以 \(S\) 集合的鑰匙到達 \(i\) 點的最小消耗。跑最短路或者 BFS (因為邊權都是 1 ,所以 BFS 其實比最短路更快)都行。改悔一下,考試的時候輸入寫錯了……輸入寫錯了……輸入寫錯了……
#include <cstdio> #include <algorithm> #include <vector> #include <queue> #include <ctype.h> using namespace std; template<typename T> void rd(T &num) { char tt; while (!isdigit(tt = getchar())); num = tt - '0'; while (isdigit(tt = getchar())) num = num * 10 + tt - '0'; return; } const int _N = 8000; const int INF = 1e9; struct data { int v, s, w; data(int v = 0, int s = 0, int w = 0): v(v), s(s), w(w) { } bool operator < (const data &tmp) const { return w > tmp.w; } }; struct edge { int v, s; edge(int v = 0, int s = 0): v(v), s(s) { } }; vector<edge> G[_N]; priority_queue<data> Q; int N, K, M, dis[_N][3000], A[_N]; void dijkstra(int beg1, int beg2) { while (!Q.empty()) Q.pop(); for (int i = 1; i <= N; ++i) for (int j = 0; j != (1 << K); ++j) dis[i][j] = INF; dis[beg1][beg2] = 0; Q.push(data(beg1, beg2, 0));//v S w while (!Q.empty()) { data p = Q.top(); Q.pop(); if (p.w > dis[p.v][p.s]) continue; for (int i = G[p.v].size() - 1; i >= 0; --i) { edge e = G[p.v][i]; if ((p.s & e.s) == e.s) { if (dis[e.v][A[e.v] | p.s] > dis[p.v][p.s] + 1) { dis[e.v][A[e.v] | p.s] = dis[p.v][p.s] + 1; Q.push(data(e.v, A[e.v] | p.s, dis[e.v][A[e.v] | p.s])); } } } } return; } int main() { rd(N), rd(M), rd(K); for (int i = 1; i <= N; ++i) for (int j = 0; j < K; ++j) { int t; rd(t); if (t) A[i] |= 1 << j; } for (int i = 1; i <= M; ++i) { int x, y, s = 0; rd(x), rd(y); for (int j = 0; j < K; ++j) { int t; rd(t); if (t) s |= 1 << j; } G[x].push_back(edge(y, s)); } dijkstra(1, A[1]); int ans = INF; for (int i = 0; i != (1 << K); ++i) ans = min(ans, dis[N][i]); if (ans < INF) printf("%d\n", ans); else printf("No Solution\n"); return 0; }
3.) 士兵訓練
分析略惡心。首先,分析可得答案是子樹內點權的嚴格次大值。然後考慮外部點權的“加成”。需要維護 \(p\) 為根的子樹內的 \(a \geq b > c\) ,以及子樹外的 \(x > y\)。然後如果 \(b + x \ne a\) ,直接更新答案就可以。否則答案應為 \(max(b + y, c + x)\) 。然後考慮怎麽維護。
法一:最直觀的想法是通過節點的 dfn 序重新編號,然後用一顆線段樹維護子樹內節點。再用前綴、後綴之類的方法維護子樹外節點(重新編號後子樹外的節點肯定就是 \([1, x]\) 和 \([y, n]\))。
法二:還是用前綴、後綴維護子樹外節點,不過直接用子樹內節點信息更新子樹的答案。
假裝這是代碼
比賽-模擬賽 (Aug 18, 2018)