1. 程式人生 > >[bzoj3747][POI2015]Kinoman_線段樹

[bzoj3747][POI2015]Kinoman_線段樹

sans PE while down typedef 如果 區間 for max

Kinoman bzoj-3747 POI-2015

題目大意:有m部電影,第i部電影的好看值為w[i]。現在放了n天電影,請你選擇一段區間l~r使得l到r之間的好看值總和最大。特別地,如果同一種電影放了兩遍及以上,那麽這種電影的好看值將不會被獲得。

註釋:$1\le m \le n \le 10^6$。

想法:和rmq problem類似的,我們處理出每一個位置pos右邊第一個和pos上電影種類相同的位置nxt[pos]。然後,我從1-n掃一遍,每次講l+1到nxt[l]-1之間的值加上w[a[l]],這個過程可以用線段樹維護。

最後,附上醜陋的代碼... ...

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 1000050
#define lson pos<<1
#define rson pos<<1|1
typedef long long ll;
ll t[N<<2],lazy[N<<2];
int f[N],w[N],nxt[N],n,m,now[N];
char nc()
{
    static char buf[100000],*p1,*p2;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd()
{
    register int x=0;
    register char s=nc();
    while(s<‘0‘||s>‘9‘)s=nc();
    while(s>=‘0‘&&s<=‘9‘)x=(x<<3)+(x<<1)+s-‘0‘,s=nc();
    return x;
}
void pushdown(int pos)
{
	if(!lazy[pos]) return;
	ll d=lazy[pos];
	lazy[lson]+=d; lazy[rson]+=d;
	t[lson]+=d; t[rson]+=d;
	lazy[pos]=0;
}
void update(int l,int r,int x,int y,ll v,int pos)
{
	if(x<=l&&y>=r)
	{
		t[pos]+=v; lazy[pos]+=v;
		return;
	}
	pushdown(pos);
	int mid=(l+r)>>1;
	if(x<=mid) update(l,mid,x,y,v,lson);
	if(y>mid) update(mid+1,r,x,y,v,rson);
	t[pos]=max(t[lson],t[rson]);
}
int main()
{
	n=rd(),m=rd();
	for(int i=1;i<=n;i++)
	{
		f[i]=rd();
	}
	for(int i=1;i<=m;i++)
	{
		w[i]=rd();
	}
	for(int i=n;i;i--)
	{
		nxt[i]=now[f[i]];
		now[f[i]]=i;
	}
	for(int i=1;i<=m;i++)
	{
		if(now[i])
		{
			if(!nxt[now[i]]) update(1,n,now[i],n,w[i],1);
			else update(1,n,now[i],nxt[now[i]]-1,w[i],1);
		}
	}
	ll ans=0;
	for(int i=1;i<=n;i++)
	{
		ans=max(ans,t[1]);
		if(nxt[i])
		{
			update(1,n,i,nxt[i]-1,-w[f[i]],1);
			if(nxt[nxt[i]]) update(1,n,nxt[i],nxt[nxt[i]]-1,w[f[i]],1);
			else update(1,n,nxt[i],n,w[f[i]],1);
		}
		else update(1,n,i,n,-w[f[i]],1);
	}
	printf("%lld\n",ans);
	return 0;
}

小結:線段樹能幹好多事情啊qwq

[bzoj3747][POI2015]Kinoman_線段樹