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

【BZOJ3747】[POI2015]Kinoman 線段樹

type ios line 解釋 class ring define 當前 都是

【BZOJ3747】[POI2015]Kinoman

Description

共有m部電影,編號為1~m,第i部電影的好看值為w[i]。 在n天之中(從1~n編號)每天會放映一部電影,第i天放映的是第f[i]部。 你可以選擇l,r(1<=l<=r<=n),並觀看第l,l+1,…,r天內所有的電影。如果同一部電影你觀看多於一次,你會感到無聊,於是無法獲得這部電影的好看值。所以你希望最大化觀看且僅觀看過一次的電影的好看值的總和。

Input

第一行兩個整數n,m(1<=m<=n<=1000000)。 第二行包含n個整數f[1],f[2],…,f[n](1<=f[i]<=m)。 第三行包含m個整數w[1],w[2],…,w[m](1<=w[j]<=1000000)。

Output

輸出觀看且僅觀看過一次的電影的好看值的總和的最大值。

Sample Input

9 4
2 3 1 1 4 1 2 4 1
5 3 6 6

Sample Output

15
樣例解釋:
觀看第2,3,4,5,6,7天內放映的電影,其中看且僅看過一次的電影的編號為2,3,4。

題解:還是用到這一個思路:每個子串都是一個前綴的後綴,那麽我們枚舉每個前綴,然後用線段樹維護它的每個後綴的答案即可。

具體地,如果當前位置是i,i的前綴是pre[i],那麽在(pre[i],i]中的後綴的和都會加上w;還要減掉原來pre[i]的權值,即在(pre[pre[i]],pre[i]]裏的後綴都要減去w。再用線段樹查詢最大值即可。

#include <cstdio>
#include <iostream>
#include <cstring>
#define lson x<<1
#define rson x<<1|1
using namespace std;
typedef long long ll;
const int maxn=1000010;
int n,m;
ll ans;
int w[maxn],f[maxn],pre[maxn],last[maxn];
ll s[maxn<<2],tag[maxn<<2];
void updata(int l,int r,int x,int a,int b,ll val)
{
	if(a<=l&&r<=b)
	{
		s[x]+=val,tag[x]+=val;
		return ;
	}
	if(tag[x])	s[lson]+=tag[x],tag[lson]+=tag[x],s[rson]+=tag[x],tag[rson]+=tag[x],tag[x]=0;
	int mid=(l+r)>>1;
	if(a<=mid)	updata(l,mid,lson,a,b,val);
	if(b>mid)	updata(mid+1,r,rson,a,b,val);
	s[x]=max(s[lson],s[rson]);
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),m=rd();
	int i;
	for(i=1;i<=n;i++)	f[i]=rd(),pre[i]=last[f[i]],last[f[i]]=i;
	for(i=1;i<=m;i++)	w[i]=rd();
	for(i=1;i<=n;i++)
	{
		updata(1,n,1,pre[i]+1,i,w[f[i]]);
		if(pre[i])	updata(1,n,1,pre[pre[i]]+1,pre[i],-w[f[i]]);
		ans=max(ans,s[1]);
	}
	printf("%lld",ans);
	return 0;
}

【BZOJ3747】[POI2015]Kinoman 線段樹