1. 程式人生 > >BZOJ3747 [POI2015]Kinoman 【線段樹】

BZOJ3747 [POI2015]Kinoman 【線段樹】

n) AR 端點 const getchar() long long getc 刪除 poi

題目鏈接

BZOJ3747

題解

這種找區間最優的問題,一定是枚舉一個端點,然後用數據結構維護另一個端點

我們枚舉左端點,用線段樹維護每個點作為右端點時的答案
當左端點為\(1\)時,我們能\(O(n)\)預處理出每個位置的答案初始化線段樹
當左端點右移一位時,該位上的電影就從區間刪除了,記\(nxt[i]\)為下一個同類電影的位置,那麽從左端點到\(nxt[i] - 1\)的位置的權值都會減少掉\(w[f[i]]\),而\(nxt[i]\)\(nxt[nxt[i]] - 1\)的位置都會增加\(w[f[i]]\)
就是一個簡單的線段樹維護區間最大值了

#include<algorithm>
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<map> #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) #define REP(i,n) for (int i = 1; i <= (n); i++) #define mp(a,b) make_pair<int,int>(a,b) #define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int> #define LL long long int #define ls (u << 1) #define rs (u << 1 | 1) using namespace std; const int maxn = 1000005,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; } LL mx[maxn << 2],tag[maxn << 2],C[maxn]; void upd(int u){mx[u] = max(mx[ls],mx[rs]);} void pd(int u){ if (tag[u]){ mx[ls] += tag[u]; tag[ls] += tag[u]; mx[rs] += tag[u]; tag[rs] += tag[u]; tag[u] = 0; } } void build(int u,int l,int r){ if (l == r){mx[u] = C[l]; return;} int mid = l + r >> 1; build(ls,l,mid); build(rs,mid + 1,r); upd(u); } void add(int u,int l,int r,int L,int R,LL v){ if (l >= L && r <= R){mx[u] += v; tag[u] += v; return;} pd(u); 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 mx[u]; pd(u); 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 max(query(ls,l,mid,L,R),query(rs,mid + 1,r,L,R)); } int nxt[maxn],last[maxn],bac[maxn]; int n,m,f[maxn],w[maxn]; LL sum,ans; int main(){ n = read(); m = read(); REP(i,n) f[i] = read(); REP(i,m) w[i] = read(); for (int i = n; i; i--) nxt[i] = last[f[i]],last[f[i]] = i; for (int i = 1; i <= n; i++){ if (!bac[f[i]]) sum += w[f[i]]; else if (bac[f[i]] == 1) sum -= w[f[i]]; bac[f[i]]++; C[i] = sum; } build(1,1,n); for (int i = 1; i <= n; i++){ ans = max(ans,query(1,1,n,i,n)); if (nxt[i]){ if (nxt[i] > i + 1) add(1,1,n,i + 1,nxt[i] - 1,-w[f[i]]); add(1,1,n,nxt[i],nxt[nxt[i]] ? nxt[nxt[i]] - 1 : n,w[f[i]]); } else add(1,1,n,i + 1,n,-w[f[i]]); } printf("%lld\n",ans); return 0; }

BZOJ3747 [POI2015]Kinoman 【線段樹】