比賽-OBlack學長的訓練賽 (25 Aug, 2018)
阿新 • • 發佈:2018-08-25
operator 結束 inline using tin con -- tdi ++ ,則在這些位置 +1 ;然後在同元素 dfn 值相鄰的兩個節點的 lca 處 -1 ,即在 \(lca_{1,3},lca_{3,10},lca_{10,20}\) 處 -1 。之後對於每個詢問,求子樹權值和即可。
A. 試卷
B. 果實
據說是另一道題的弱化版,原題帶修改,好像需要用 set 維護節點。這題數據非常友善,可以用莫隊水過(塊長卡了 \(n^{\frac{1}{2}}\) 但沒卡 \(n^{\frac{2}{3}}\) )。
問題可以用 dfn 序轉化為求區間不同元素個數。離線做。將詢問根據左端點排序,然後維護一個 BIT ,在某個元素第一次出現的位置處 +1 。然後在getsum 就可以得到答案了。左端點右移時,把 BIT 上當前位置 -1 ,在當前位置元素下一次出現的位置(用類似鏈式前向星的方法預處理) +1 即可。
然而考試結束後 OBlack 說在線更好做。樹上差分即可。比如某元素的出現的 dfn 值分別為 \(1,3,10,20\)
離線 BIT 做法
#include <cstdio> #include <algorithm> #include <ctype.h> #include <vector> using namespace std; char *p1, *p2, buf[1 << 20], sss[50]; inline char gc() { return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?EOF:*p1++; } template<typename T> void rd(T &num) { char tt; while (!isdigit(tt = gc())); num = tt - '0'; while (isdigit(tt = gc())) num = num * 10 + tt - '0'; return; } template<typename T> void pt(T num) { int top = 0; do sss[++top] = num % 10 + '0'; while (num /= 10); while (top) putchar(sss[top--]); putchar('\n'); return; } const int _N = 501000; vector<int> G[_N]; int dfn[_N], siz[_N], A[_N], B[_N], bit[_N], nxt[_N], fst[_N], ans[_N]; int Time, N, M, C; struct data { int id, v; bool operator < (const data &tmp) const { return dfn[v] < dfn[tmp.v]; } } Q[_N]; void build(int p, int dad) { dfn[p] = ++Time, siz[p] = 1, B[dfn[p]] = A[p]; for (int i = G[p].size() - 1; i >= 0; --i) { int v = G[p][i]; if (v != dad) build(v, p), siz[p] += siz[v]; } return; } void add(int k, int d) { for (int i = k; i <= N; i += i & -i) bit[i] += d; return; } int getsum(int k) { int sum = 0; for (int i = k; i; i ^= i & -i) sum += bit[i]; return sum; } int main() { rd(N), rd(M), rd(C); for (int i = 1; i <= N; ++i) rd(A[i]); for (int a, b, i = 1; i < N; ++i) { rd(a), rd(b); G[a].push_back(b), G[b].push_back(a); } build(1, 0); for (int i = N; i >= 1; --i) nxt[i] = fst[B[i]], fst[B[i]] = i; for (int i = 1; i <= N; ++i) if (fst[B[i]] == i) add(i, 1); for (int t, i = 1; i <= M; ++i) rd(Q[i].v), Q[i].id = i; sort(Q + 1, Q + 1 + M); int pos = 1; for (int i = 1; i <= M; ++i) { while (pos < dfn[Q[i].v]) { add(pos, -1); if (nxt[pos]) add(nxt[pos], 1); ++pos; } ans[Q[i].id] = getsum(dfn[Q[i].v] + siz[Q[i].v] - 1); } for (int i = 1; i <= M; ++i) pt(ans[i]); return 0; }
C. 旅行
#include <cstdio> #include <algorithm> #include <ctype.h> #include <vector> #include <queue> using namespace std; char *p1, *p2, buf[1 << 20], sss[50]; inline char gc() { return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin))==p1?EOF:*p1++; } template<typename T> void rd(T &num) { char tt; while (!isdigit(tt = gc())); num = tt - '0'; while (isdigit(tt = gc())) num = num * 10 + tt - '0'; return; } template<typename T> void pt(T num) { int top = 0; do sss[++top] = num % 10 + '0'; while (num /= 10); while (top) putchar(sss[top--]); putchar('\n'); return; } const int _N = 51000; struct edge { int v, w; edge(int v = 0, int w = 0): v(v), w(w) { } bool operator < (const edge &tmp) const { return w > tmp.w; } }; priority_queue<edge> Q; vector<edge> G[_N]; int dis[_N]; int N, M, ans = -1; int dijkstra(int lim, int t) { for (int i = 0; i <= N; ++i) dis[i] = -1; dis[1] = 0, Q.push(edge(1, 0)); while (!Q.empty()) { edge p = Q.top(); Q.pop(); if (p.w != dis[p.v]) continue; for (int i = G[p.v].size() - 1; i >= 0; --i) { edge v = G[p.v][i]; if (p.v == 1 && v.v >> lim & 1 ^ t) continue; if (v.v == 0 && p.v >> lim & 1 ^ t ^ 1) continue; if (dis[v.v] == -1 || dis[v.v] > dis[p.v] + v.w) Q.push(edge(v.v, dis[v.v] = dis[p.v] + v.w)); } } return dis[0]; } int main() { rd(N), rd(M); for (int x, y, c, d, i = 1; i <= M; ++i) { rd(x), rd(y), rd(c), rd(d); if (x > y) swap(x, y), swap(c, d); G[x].push_back(edge(y, c)); if (x == 1) x = 0; G[y].push_back(edge(x, d)); } int tmp = ,N cnt = 0; while (tmp) ++cnt, tmp >>= 1; for (int i = 0; i < cnt; ++i) { int t = dijkstra(i, 0); if (t != -1 && (ans == -1 || ans > t)) ans = t; t = dijkstra(i, 1); if (t != -1 && (ans == -1 || ans > t)) ans = t; } pt(ans); return 0; }
比賽-OBlack學長的訓練賽 (25 Aug, 2018)