1. 程式人生 > >BZOJ4826 [Hnoi2017]影魔 【線段樹 + 單調棧】

BZOJ4826 [Hnoi2017]影魔 【線段樹 + 單調棧】

getchar using clas urn 端點 鏈接 push std Go

題目鏈接

BZOJ4826

題解

蒟蒻智力水平捉急orz

我們會發現相鄰的\(i\)\(j\)貢獻一定是\(p1\),可以很快算出來【然而我一開始忘了考慮調了半天】

我們現在只考慮不相鄰的
我們只需要找出所有產生貢獻的\(i,j\)即可
我們發現每一個產生貢獻的\(i,j\)都能對應到一個三元組\((i,k,j)\),分別對應區間的最大值,次大值,第三大值
我們枚舉中間位置\(i\),找到\(i\)左邊第一個比\(i\)大的位置\(L[i]\),右邊第一個比\(i\)大的位置\(R[i]\)
那麽\(L[i]\)\(R[i]\)的貢獻就是\(p1\)
區間\((L[i],i)\)\(R[i]\)

的貢獻是\(p2\)
區間\((i,R[i])\)\(L[i]\)的貢獻是\(p2\)
一對點對詢問產生貢獻,當且僅當其都在區間中
所以我們可以用一個端點去儲存貢獻,而另一個端點作為更新的位置
然後再離線詢問,用到\(r\)端點時區間\([l,r]\)的貢獻減去到\(l - 1\)時區間\([l,r]\)的貢獻,就是答案

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<vector>
#include<cmath>
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define LL long long int #define ls (u << 1) #define rs (u << 1 | 1) using namespace std; const int maxn = 200005,maxm = 100005,INF = 1000000000; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48
&& c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } struct node{int l,r;}; struct Que{int l,r,pos,t,id;}q[maxn << 1]; vector<node> Ln[maxn],Rn[maxn]; LL ans[maxn]; LL A[maxn],n,m,p1,p2,qi; LL st[maxn],top,Li[maxn],Ri[maxn]; LL sum[maxn << 2],tag[maxn << 2]; inline bool operator <(const Que& a,const Que& b){ return a.pos < b.pos; } void upd(int u){sum[u] = sum[ls] + sum[rs];} void pd(int u,int l,int r){ if (tag[u]){ int mid = l + r >> 1; sum[ls] += tag[u] * (mid - l + 1); sum[rs] += tag[u] * (r - mid); tag[ls] += tag[u]; tag[rs] += tag[u]; tag[u] = 0; } } void add(int u,int l,int r,int L,int R,LL v){ if (l >= L && r <= R){sum[u] += v * (r - l + 1); tag[u] += v; return;} pd(u,l,r); int mid = l + r >> 1; if (mid >= L) add(ls,l,mid,L,R,v); if (mid < R) add(rs,mid + 1,r,L,R,v); upd(u); } LL query(int u,int l,int r,int L,int R){ if (l >= L && r <= R) return sum[u]; pd(u,l,r); int mid = l + r >> 1; if (mid >= R) return query(ls,l,mid,L,R); if (mid < L) return query(rs,mid + 1,r,L,R); return query(ls,l,mid,L,R) + query(rs,mid + 1,r,L,R); } void init(){ for (int i = 1; i <= n; i++){ while (top && A[i] > A[st[top]]) top--; Li[i] = st[top]; st[++top] = i; } st[0] = n + 1; top = 0; for (int i = n; i; i--){ while (top && A[i] > A[st[top]]) top--; Ri[i] = st[top]; st[++top] = i; } for (int i = 1; i <= n; i++){ //printf("pos: %d L:[%d,%d] R:[%d,%d]\n",i,Li[i] + 1,i - 1,i + 1,Ri[i] - 1); if (Li[i]) Ln[Li[i]].push_back((node){i + 1,Ri[i] - 1}); if (Ri[i] <= n) Rn[Ri[i]].push_back((node){Li[i] + 1,i - 1}); } } void solve(){ sort(q + 1,q + 1 + qi); int l,r,pos = 1; for (int i = 0; i <= n; i++){ for (unsigned int j = 0; j < Ln[i].size(); j++){ l = Ln[i][j].l; r = Ln[i][j].r; if (l <= r) add(1,1,n,l,r,p2); } for (unsigned int j = 0; j < Rn[i].size(); j++){ l = Rn[i][j].l; r = Rn[i][j].r; if (l <= r) add(1,1,n,l,r,p2); if (l - 1 > 0) add(1,1,n,l - 1,l - 1,p1); } while (pos <= qi && q[pos].pos == i){ ans[q[pos].id] += query(1,1,n,q[pos].l,q[pos].r) * q[pos].t; pos++; } } for (int i = 1; i <= m; i++) printf("%lld\n",ans[i]); } int main(){ n = read(); m = read(); p1 = read(); p2 = read(); int l,r; for (int i = 1; i <= n; i++) A[i] = read(); for (int i = 1; i <= m; i++){ l = read(); r = read(); q[++qi] = (Que){l,r,l - 1,-1,i}; q[++qi] = (Que){l,r,r,1,i}; ans[i] += 1ll * (r - l) * p1; } init(); solve(); return 0; }

BZOJ4826 [Hnoi2017]影魔 【線段樹 + 單調棧】