1. 程式人生 > >[BZOJ3747][POI2015]Kinoman(線段樹)

[BZOJ3747][POI2015]Kinoman(線段樹)

Address

洛谷P3582
BZOJ3747

Solution

  • 先考慮如果固定右端點 r = n r=n 怎麼做
  • 考慮對於每個 i
    i
    ,給第 i i 天一個權值 a i
    a_i
  • 如果在第 i i 天之後沒有再次與第 i i 天放映同樣的電影,那麼 a
    i = w f i a_i=w_{f_i}
  • 如果在第 i i 天之後再一次且僅一次放映與第 i i 天相同的電影,那麼 a i = w f i a_i=-w_{f_i}
  • 否則 a i = 0 a_i=0
  • 顯然,這樣 i = x n a i \sum_{i=x}^na_i 就是區間 [ x , n ] [x,n] 的答案
  • 右端點不一定是 n n ,所以我們考慮從左往右動態地移動 r r
  • 當右端點 r r x 1 x-1 移動到 x x
  • y y 為滿足 y < x y<x f y = f x f_y=f_x 的最大的 y y
  • z z 為滿足 z < x z<x f z = f x f_z=f_x 的次大的 z z
  • 那麼最多隻有 a x a_x a y a_y (如果 y y 存在)、 a z a_z (如果 z z 存在)三個值會發生變化
  • 這時候 i = k x a i \sum_{i=k}^xa_i 是區間 [ k , x ] [k,x] 的答案
  • 相當於查詢 a a 的長度為 x x 的字首的最大字尾和
  • 線段樹完美解決
  • 複雜度 O ( n log n ) O(n\log n)
  • 常數巨大

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;
}