4826: [Hnoi2017]影魔
4826: [Hnoi2017]影魔
https://lydsy.com/JudgeOnline/problem.php?id=4826
分析:
莫队+单调栈+st表。
考虑如何O(1)加入一个点,删除一个点,类似bzoj4540。然后就可以莫队了。复杂度$O(n\sqrt n)$
代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cctype> 7 #include<set> 8 #include<queue> 9 #include<vector> 10 #include<map> 11 using namespace std; 12 typedef long long LL; 13 14 char buf[100000], *p1 = buf, *p2 = buf; 15 #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++) 16 inline int read() { 17 int x=0,f=1;char ch=nc();for(;!isdigit(ch);ch=nc())if(ch=='-')f=-1; 18 for(;isdigit(ch);ch=nc())x=x*10+ch-'0';return x*f; 19 } 20 21 const int N = 200005; 22 struct Edge{ 23 int l, r, bel, id; 24 bool operator < (const Edge &A) const { 25 return bel == A.bel ? r < A.r : bel < A.bel; 26 } 27 }Q[N]; 28 int cnt[N], a[N], f[N][22], Log[N]; 29 int sum1R[N], sum1L[N], sum2R[N], sum2L[N], q[N], mxL[N], mxR[N]; 30 int P1, P2, L, R, n, m; 31 LL ans[N], Ans = 0; 32 33 int getmax(int l,int r) { 34 int k = Log[r - l + 1]; 35 int p1 = f[l][k], p2 = f[r - (1 << k) + 1][k]; 36 return a[p1] > a[p2] ? p1 : p2; 37 } 38 39 void updR(int x,int f) { 40 if (L >= R) { Ans = 0; return ; } 41 int p = getmax(L, R - 1), p2 = max(mxL[x], p); 42 Ans += (sum1L[R - 1] - sum1L[p2] + 1) * P1 * f; 43 Ans += (sum1L[p2] - sum1L[p]) * P2 * f; 44 Ans += (sum2L[R - 1] - sum2L[p2]) * P2 * f; 45 if (L > mxL[x]) Ans += (p2 - L) * P2 * f; 46 } 47 48 void updL(int x,int f) { 49 if (L >= R) { Ans = 0; return ; } 50 int p = getmax(L + 1, R), p2 = min(mxR[x], p); 51 Ans += (sum1R[L + 1] - sum1R[p2] + 1) * P1 * f; 52 Ans += (sum1R[p2] - sum1R[p]) * P2 * f; 53 Ans += (sum2R[L + 1] - sum2R[p2]) * P2 * f; 54 if (R < mxR[x]) Ans += (R - p2) * P2 * f; 55 } 56 57 int main() { 58 n = read(), m = read();P1 = read(), P2 = read(); 59 int B = sqrt(n); 60 for (int i = 1; i <= n; ++i) a[i] = read(), f[i][0] = i; 61 for (int j = 1; (1 << j) <= n; ++j) { 62 for (int i = 1; i + (1 << j) - 1 <= n; ++i) { 63 int p1 = f[i][j - 1], p2 = f[i + (1 << (j - 1))][j - 1]; 64 f[i][j] = a[p1] > a[p2] ? p1 : p2; 65 } 66 } 67 Log[0] = -1; 68 for (int i = 1; i <= n; ++i) Log[i] = Log[i >> 1] + 1; 69 a[0] = a[n + 1] = 1e9; 70 int l = 1, r = 1; q[1] = 0; 71 for (int i = 1; i <= n; ++i) { 72 while (l <= r && a[i] > a[q[r]]) r --; 73 q[++r] = i; 74 int j = q[r - 1]; mxL[i] = j; 75 sum2L[i] = sum2L[j] + (i - j - 1); sum1L[i] = sum1L[j] + 1; 76 } 77 l = 1, r = 1; q[1] = n + 1; 78 for (int i = n; i >= 1; --i) { 79 while (l <= r && a[i] > a[q[r]]) r --; 80 q[++r] = i; 81 int j = q[r - 1]; mxR[i] = j; 82 sum2R[i] = sum2R[j] + (j - i - 1); sum1R[i] = sum1R[j] + 1; 83 } 84 for (int i = 1; i <= m; ++i) { 85 Q[i].l = read(), Q[i].r = read(), Q[i].bel = (Q[i].l - 1) / B + 1, Q[i].id = i; 86 } 87 sort(Q + 1, Q + m + 1); 88 L = 1, R = 0; 89 for (int i = 1; i <= m; ++i) { 90 while (L > Q[i].l) L --, updL(L, 1); 91 while (R < Q[i].r) R ++, updR(R, 1); 92 while (L < Q[i].l) updL(L, -1), L ++; 93 while (R > Q[i].r) updR(R, -1), R --; 94 ans[Q[i].id] = Ans; 95 } 96 for (int i = 1; i <= m; ++i) printf("%lld\n", ans[i]); 97 return 0; 98 }
sol2:
每个点i找到左边右边第一个比它大的点,设为x,y,那么x,y会产生P1的贡献,x和[i+1,y-1],y和[x+1,i-1]会产生P2的贡献,把它们看做是平面上的点,然后转化为二维数点问题。
询问区间L,R,就是询问横坐标在[L,R]的,纵坐标也在[L,R]的点有多少个。有一个问题:(y,x+1)这个点是否和(x+1,y)的贡献一样?就是这两个数哪个在前面的问题,因为询问总是一个关于直线y=x对称的一个矩形,而(y,x+1)和(x+1,y)也关于y=x对称,所以这两个点怎么放都行。
然后离线+扫描线+线段树就行了(可以标记永久化)。复杂度$O(nlogn)$
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cctype> 7 #include<set> 8 #include<queue> 9 #include<vector> 10 #include<map> 11 #define Root 1, n, 1 12 #define lson l, mid, rt << 1 13 #define rson mid + 1, r, rt << 1 | 1 14 using namespace std; 15 typedef long long LL; 16 17 inline int read() { 18 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 19 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 20 } 21 22 const int N = 200005; 23 struct Node{ 24 int p, l, r, id, w; 25 bool operator < (const Node &A) const { return p < A.p; } 26 }q[N * 2], A[N * 3]; 27 int L[N], R[N], sk[N], a[N]; 28 LL sum[N << 2], tag[N << 2], ans[N]; 29 30 void update(int l,int r,int rt,int L,int R,int v) { 31 sum[rt] += 1ll * (R - L + 1) * v; 32 if (L == l && r == R) { 33 tag[rt] += v; return ; 34 } 35 int mid = (l + r) >> 1; 36 if (R <= mid) update(lson, L, R, v); 37 else if (L > mid) update(rson, L, R, v); 38 else update(lson, L, mid, v), update(rson, mid + 1, R, v); 39 } 40 LL query(int l,int r,int rt,int L,int R,LL t) { 41 if (L == l && r == R) return sum[rt] + 1ll * (R - L + 1) * t; 42 int mid = (l + r) >> 1; 43 if (R <= mid) return query(lson, L, R, t + tag[rt]); 44 else if (L > mid) return query(rson, L, R, t + tag[rt]); 45 else return query(lson, L, mid, t + tag[rt]) + query(rson, mid + 1, R, t + tag[rt]); 46 } 47 int main() { 48 int n = read(), m = read(), P1 = read(), P2 = read(); 49 for (int i = 1; i <= n; ++i) a[i] = read(); 50 for (int top = 0, i = 1; i <= n; ++i) { 51 while (top && a[i] > a[sk[top]]) top --; 52 sk[++top] = i; 53 L[i] = sk[top - 1]; 54 } 55 sk[0] = n + 1; 56 for (int top = 0, i = n; i >= 1; --i) { 57 while (top && a[i] > a[sk[top]]) top --; 58 sk[++top] = i; 59 R[i] = sk[top - 1]; 60 } 61 int tot = 0; 62 for (int i = 1; i <= n; ++i) { 63 if (L[i] && R[i] <= n) A[++tot] = (Node){R[i], L[i], L[i], 0, P1}; 64 if (L[i] && R[i] > i + 1) A[++tot] = (Node){L[i], i + 1, R[i] - 1, 0, P2}; 65 if (R[i] <= n && i > L[i] + 1) A[++tot] = (Node){R[i], L[i] + 1, i - 1, 0, P2}; 66 } 67 for (int i = 1; i <= m; ++i) { 68 int l = read(), r = read(); 69 q[i] = (Node){l - 1, l, r, i, -1}; 70 q[i + m] = (Node){r, l, r, i, 1}; 71 ans[i] += 1ll * (r - l) * P1; 72 } 73 sort(q + 1, q + m + m + 1); 74 sort(A + 1, A + tot + 1); 75 int now = 1; 76 while (A[now].p <= 0) now ++; 77 for (int i = 1; i <= m + m; ++i) { 78 while (now <= tot && A[now].p <= q[i].p) { 79 update(Root, A[now].l, A[now].r, A[now].w); now ++; 80 } 81 ans[q[i].id] += 1ll * query(Root, q[i].l, q[i].r, 0) * q[i].w; 82 } 83 for (int i = 1; i <= m; ++i) printf("%lld\n", ans[i]); 84 return 0; 85 }