ZJOI2018 胖

mac2022-06-30  154

ZJOI2018 胖

题意:

题目传送门

题解:

时隔一年才来做这道题的我真是太菜了…… 我们将修建的道路所连接的点称作关键点,那么考虑每一条道路能够更新的点一定是一段连续的区间\([L, R]\)\(\sum_i R_i - L_i + 1\)就是题目的答案。于是我们考虑如何求出每一个关键点更新的区间\([L, R]\)。很容易想到的是二分,由于右端点与左端点的方法类似,我们就以左端点为例。

假设当前二分到的点为\(p\),那么如果\(p\)会被当前关键点\(x\)更新到,那么一定满足\([p - d, p + d]\)\(d\)\(x\)\(p\)的下标差值,即\(| x - p |\))这个区间内没有关键点到达\(p\)的距离比\(x\)更小。如果不满足,那么更新这个点\(p\)的一定是另一个关键点。于是我们转而考虑如何维护区间最小值,可以用线段树,但是常数太大,我们可以用\(ST\)表来完成。每次预处理为\(O(k log(k))\),查询\(O(1)\),这样总复杂度为\(O(k log(k))\)

另外还要考虑到如果\([p - d, p + d]\)这个区间关键点到达\(p\)的距离等于\(x\)\(p\)的距离时,这个贡献只能算\(1\)次,显然如果两个关键点到\(p\)的下标差值不同,那么贡献一定是算在下标差值小的关键点上,如果这个值也相同,那么随便选一个都行,只要答案统计时不重复就行了。

Code

#pragma GCC optimize (2,"inline","Ofast") #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5 + 50; const ll INF = 1e16; int n, m, k; int w[N], Lg[N]; ll sum[N], st1[N][21], st2[N][21], lmn[N], rmn[N]; struct edge { int pos, l; bool operator < (const edge &rhs) const { return pos < rhs.pos; } }E[N]; int Min(int x, int y) { if(rmn[x] == rmn[y]) return min(x, y); return rmn[x] < rmn[y] ? x : y; } int Max(int x, int y) { if(lmn[x] == lmn[y]) return max(x, y); return lmn[x] > lmn[y] ? x : y; } int GetMn(int l, int r) { int G = Lg[r - l + 1]; return Min(st2[l][G], st2[r - (1 << G) + 1][G]); } int GetmMx(int l, int r) { int G = Lg[r - l + 1]; return Max(st1[l][G], st1[r - (1 << G) + 1][G]); } void Init() { sort(E + 1, E + 1 + k); for(int i = 1; i <= k; i++) { lmn[i] = sum[E[i].pos] - E[i].l; rmn[i] = sum[E[i].pos] + E[i].l; st1[i][0] = st2[i][0] = i; } for(int j = 1; (1 << j) <= k; j++) { for(int i = 1; i + (1 << j) - 1 <= k; i++) { st1[i][j] = Max(st1[i][j - 1], st1[i + (1 << (j - 1))][j - 1]); st2[i][j] = Min(st2[i][j - 1], st2[i + (1 << (j - 1))][j - 1]); } } } int Judge(int a, int b, int p) { ll len1 = E[a].l + abs(sum[p] - sum[E[a].pos]), len2 = E[b].l + abs(sum[p] - sum[E[b].pos]); if(len1 != len2) return len1 < len2 ? a : b; int dis1 = abs(E[a].pos - p), dis2 = abs(E[b].pos - p); if(dis1 != dis2) return dis1 < dis2 ? a : b; return min(a, b); } int Check(int a, int p) { int dis = abs(E[a].pos - p), dom = a; edge e1 = (edge) { p, 0 }, e2 = (edge) { p + dis, 0 }, e3 = (edge) { p - dis, 0}; int l = lower_bound(E + 1, E + 1 + k, e3) - E, r = upper_bound(E + 1, E + 1 + k, e2) - E - 1, mid = upper_bound(E + 1, E + 1 + k, e1) - E - 1; if(l <= mid) dom = Judge(dom, GetMx(l, mid), p); if(mid < r) dom = Judge(dom, GetMn(mid + 1, r), p); return dom == a; } void Solve() { ll ans = 0; for(int i = 1; i <= k; i++) { int l = 1, r = E[i].pos, ansl = E[i].pos, ansr = E[i].pos; while(l <= r) { int mid = (l + r) >> 1; if(Check(i, mid)) r = mid - 1, ansl = mid; else l = mid + 1; } l = E[i].pos, r = n; while(l <= r) { int mid = (l + r) >> 1; if(Check(i, mid)) l = mid + 1, ansr = mid; else r = mid - 1; } ans += ansr - ansl + 1; } printf("%lld\n", ans); } int main() { scanf("%d%d", &n, &m); for(int i = 2; i <= n; i++) Lg[i] = Lg[i >> 1] + 1; for(int i = 2; i <= n; i++) scanf("%d", &w[i]), sum[i] = sum[i - 1] + w[i]; for(int i = 1; i <= m; i++) { scanf("%d", &k); for(int j = 1; j <= k; j++) scanf("%d%d", &E[j].pos, &E[j].l); Init(); Solve(); } return 0; }

转载于:https://www.cnblogs.com/Apocrypha/p/10562586.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)