BZOJ 3747: [POI2015]Kinoman

题目大意:每个点有一个权值和一个颜色,每种颜色有一个得分,每个区间的得分为这个区间内只出现过一次的颜色的得分总和,求最大得分

说个O(nlogn)的做法吧
先预处理出对于每个节点,下一个跟他相同颜色的点的位置
然后枚举最后选的那个区间的左端点r,用一颗线段树记录对于每个r,这个区间的得分是多少
当左端点l向右移时,设当前左端点颜色为i,相当于把以l到nxt[i]-1为结尾的区间的得分减w[i],再把以nxt[i]到nxt[nxt[i]]-1为结尾的区间的得分加w[i]
然后查询一下以l+1到n为结尾的区间的得分的最大值,就是以l为为开头的所有区间的得分的最大值

速度奇慢无比
不知道有没有O(N)的做法

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define lll long long
using namespace std;
int l[4000001],r[4000001];
lll t[4000001],w[4000001];
void build(lll now,lll ll,lll rr)
{
    l[now]=ll;r[now]=rr;
    if(ll==rr) return;
    lll mid=(ll+rr)>>1;
    build(now<<1,ll,mid);
    build(now<<1|1,mid+1,rr);
}
void pdn(lll x)
{
    t[x<<1]+=t[x];w[x<<1]+=t[x];
    t[x<<1|1]+=t[x];w[x<<1|1]+=t[x];
    t[x]=0;
}
void pup(lll x)
{
	w[x]=max(w[x<<1],w[x<<1|1]);
}
void change(lll now,lll ll,lll rr,lll v)
{
    if(l[now]==ll&&r[now]==rr)
    {
        t[now]+=v;
        w[now]+=v;
        return;
    }
    if(t[now]) pdn(now);
    lll mid=(l[now]+r[now])>>1;
    if(rr<=mid) change(now<<1,ll,rr,v);
    else if(ll>mid) change(now<<1|1,ll,rr,v);
    else
    {
        change(now<<1,ll,mid,v);
        change(now<<1|1,mid+1,rr,v);
    }
    pup(now);
}
lll check(lll now,lll ll,lll rr)
{
    if(l[now]==ll&&r[now]==rr) return w[now];
    if(t[now]) pdn(now);
    lll mid=(l[now]+r[now])>>1;
    if(rr<=mid) return check(now<<1,ll,rr);
    else if(ll>mid) return check(now<<1|1,ll,rr);
    else return max(check(now<<1,ll,mid),check(now<<1|1,mid+1,rr));
}
int a[1000001],nxt[1000001],pre[1000001];
lll b[1000001];
int main()
{
    lll n,m;
    scanf("%lld%lld",&n,&m);
    lll i,j;
    for(i=1;i<=n;i++)
    scanf("%d",&a[i]);
    for(i=n;i>=1;i--)
    {
    	nxt[i]=pre[a[i]];
    	pre[a[i]]=i;
    }
    build(1,1,n);
    for(i=1;i<=m;i++)
    {
		scanf("%lld",&b[i]);
		if(pre[i])
		{
			if(nxt[pre[i]]) change(1,pre[i],nxt[pre[i]]-1,b[i]);
			else change(1,pre[i],n,b[i]);
		}
    }
    lll ans=0;
    for(i=1;i<=n;i++)
    {
    	ans=max(ans,check(1,i,n));
    	if(nxt[i])
    	{
    		change(1,i,nxt[i]-1,-b[a[i]]);
			if(nxt[nxt[i]]) change(1,nxt[i],nxt[nxt[i]]-1,b[a[i]]);
			else change(1,nxt[i],n,b[a[i]]);
		}
		else change(1,i,n,-b[a[i]]);
	}
	printf("%lld",ans);
}






你可能感兴趣的:(BZOJ 3747: [POI2015]Kinoman)