您的位置:首页 > 其它

【bzoj3747】[POI2015]Kinoman 线段树

2016-01-07 08:21 260 查看
经典做法,处理出next[i]表示下一个相同的地方。

注意是算出现且仅出现一次的地方。

每次加一个颜色就是[i,next[i]-1]这个区间+价值

删除一个颜色就是[i,next[i-1]]这个区间-价值

所以用一个线段树维护每个位置的答案,支持区间加和查询就可以了。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 1000100 

using namespace std;

struct yts
{
	long long tag,mx;
	int l,r;
}t[4*maxn];

int a[maxn],last[maxn],next[maxn];
long long b[maxn];
int n,m;
long long ans;

void build(int i,int l,int r)
{
	t[i].l=l;t[i].r=r;
	if (l==r) return;
	int mid=(l+r)/2;
	build(i*2,l,mid);
	build(i*2+1,mid+1,r);
}

void add(int i,long long d)
{
	t[i].mx+=d;t[i].tag+=d;
}

void release(int i)
{
	if (t[i].l==t[i].r) return;
	if (t[i].tag)
	{
		add(i*2,t[i].tag);add(i*2+1,t[i].tag);
		t[i].tag=0;
	}
}

long long query(int i,int l,int r)
{
	if (l<=t[i].l && t[i].r<=r) return t[i].mx;
	release(i);
	int mid=(t[i].l+t[i].r)/2;
	long long ans=0;
	if (l<=mid) ans=max(ans,query(i*2,l,r));
	if (mid<r) ans=max(ans,query(i*2+1,l,r));
	return ans;
}

void modify(int i,int l,int r,long long d)
{
	if (l<=t[i].l && t[i].r<=r) {add(i,d);return;}
	release(i);
	int mid=(t[i].l+t[i].r)/2;
	if (l<=mid) modify(i*2,l,r,d);
	if (mid<r) modify(i*2+1,l,r,d);
	t[i].mx=max(t[i*2].mx,t[i*2+1].mx);
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		next[last[a[i]]]=i;
		last[a[i]]=i;
	}
	for (int i=1;i<=m;i++) next[last[i]]=n+1;
	for (int i=1;i<=m;i++) scanf("%lld",&b[i]);
	build(1,1,n);
	memset(last,0,sizeof(last));
	for (int i=1;i<=n;i++)
	  if (!last[a[i]])
	  {
	  	modify(1,i,next[i]-1,b[a[i]]);
	  	last[a[i]]=i;
	  }
	for (int i=1;i<=n;i++)
	{
		ans=max(ans,query(1,i,n));
		modify(1,i,next[i]-1,-b[a[i]]);
		if (next[i]!=n+1) modify(1,next[i],next[next[i]]-1,b[a[i]]);
	}
	printf("%lld\n",ans);
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: