[BZOJ3747][POI2015]Kinoman(線段樹)
阿新 • • 發佈:2018-11-23
Address
Solution
- 先考慮如果固定右端點 怎麼做
- 考慮對於每個 ,給第 天一個權值
- 如果在第 天之後沒有再次與第 天放映同樣的電影,那麼
- 如果在第 天之後再一次且僅一次放映與第 天相同的電影,那麼
- 否則
- 顯然,這樣 就是區間 的答案
- 右端點不一定是 ,所以我們考慮從左往右動態地移動
- 當右端點 從 移動到 時
- 設 為滿足 且 的最大的
- 為滿足 且 的次大的
- 那麼最多隻有 、 (如果 存在)、 (如果 存在)三個值會發生變化
- 這時候 是區間 的答案
- 相當於查詢 的長度為 的字首的最大字尾和
- 線段樹完美解決
- 複雜度
常數巨大
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define p2 p << 1
#define p3 p << 1 | 1
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
template <class T>
T Max(T a, T b) {return a > b ? a : b;}
typedef long long ll;
const int N = 1e6 + 5, M = N << 2;
int n, m, f[N], w[N], lst[N], lst2[N];
ll ans;
struct node
{
ll sum, maxsuf;
friend inline node operator + (node a, node b)
{
return (node) {a.sum + b.sum, Max(b.maxsuf, a.maxsuf + b.sum)};
}
} T[M];
void change(int l, int r, int pos, int val, int p)
{
if (l == r)
{
T[p].sum = val;
T[p].maxsuf = val >= 0 ? val : 0;
return;
}
int mid = l + r >> 1;
if (pos <= mid) change(l, mid, pos, val, p2);
else change(mid + 1, r, pos, val, p3);
T[p] = T[p2] + T[p3];
}
node ask(int l, int r, int s, int e, int p)
{
if (l == s && r == e) return T[p];
int mid = l + r >> 1;
if (e <= mid) return ask(l, mid, s, e, p2);
else if (s >= mid + 1) return ask(mid + 1, r, s, e, p3);
else return ask(l, mid, s, mid, p2)
+ ask(mid + 1, r, mid + 1, e, p3);
}
int main()
{
int i;
n = read(); m = read();
For (i, 1, n) f[i] = read();
For (i, 1, m) w[i] = read();
For (i, 1, n)
{
change(1, n, i, w[f[i]], 1);
if (lst[f[i]])
{
change(1, n, lst[f[i]], -w[f[i]], 1);
if (lst2[f[i]]) change(1, n, lst2[f[i]], 0, 1);
}
ans = Max(ans, ask(1, n, 1, i, 1).maxsuf);
lst2[f[i]] = lst[f[i]]; lst[f[i]] = i;
}
std::cout << ans << std::endl;
return 0;
}