【杂题】[BZOJ4709]【JSOI2011】柠檬

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hzj1054689699/article/details/83578503

Description

有一个长度为n的序列a
你需要这个序列分成若干段,每个段可以任意指定一个数t,设v为t在这段中出现的次数,这一段的收益就是 v 2 t v^2*t
求最大的总收益和
n 100000 , a i 10000 n\leq 100000,ai\leq 10000

Solution

显而易见的是,最优情况下任何一段的开头和结尾的数都是相同的,且都是我们对这一段指定的这个数,否则不妨让他自成一段都会更优。

那么考虑DP
F [ i ] F[i] 表示已经做完了前i个数,最后一段以位置i结尾的最大收益

考虑转移,假设它要从位置x转移而来,那么 x i , a [ x ] = a [ i ] , F [ i ] = max ( ( c n t [ i ] c n t [ x ] + 1 ) 2 a [ i ] + F [ x 1 ] ) \forall x\leq i,a[x]=a[i],F[i]=\max\left((cnt[i]-cnt[x]+1)^2*a[i]+F[x-1]\right)

其中 c n t [ i ] cnt[i] 为1到i中a[i]出现次数

我们发现,对于相同的a,总是越往前的增长越快。

也就是说决策是具有单调性的。

我们只考虑相同的a,建立直角坐标系设横坐标为出现次数,纵坐标为贡献。

那么一个决策(x)对应在坐标系中就是一个开口向上的二次函数
对于相同的a,我们可以用一个单调栈,来维护这个东西(类比单调队列维护下凸壳)
明显越往栈顶它的增长越慢,只要更下面的决策在某一个时间点优于上面的决策,那以后一直都是更优的。

在i入栈前,考虑栈顶决策对应的曲线与栈顶下一个决策对应的曲线的交点(即栈顶下一个决策何时优于栈顶决策)和栈顶决策曲线与决策i曲线的交点

若前者时间比后者时间更靠前,说明栈顶决策没用了(要么就是决策i更优,要么就是栈顶下一个更优了),此时将栈顶弹掉。
求两个决策的交点可以采用二分。
弹到不能弹为止,将i入栈

此时还需要判断栈顶下一个决策在当前x=cnt[i]横坐标是否已经超过了栈顶,是的话将栈顶弹掉,一直弹到不能弹为止。

这时直接取栈顶转移即可
总的复杂度是 O ( n log n ) O(n\log n)

Code

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <vector>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 100005
#define LL long long
using namespace std;
LL f[N],a[N],cnt[N];
int le[10005],n,c1[10005];
vector<int>d[10005];
inline LL sqr(LL x)
{
	return x*x;
}
LL get(int x,int i)
{
	return f[x-1]+sqr(i-cnt[x]+1)*a[x];
}
LL fd(int x,int i)
{
	int l=1,r=n+1;
	while(l+1<r)
	{
		int mid=(l+r)>>1;
		if(get(x,mid)>=get(i,mid)) r=mid;
		else l=mid;
	}
	if(get(x,l)>=get(i,l)) return l;
	else return r;
}
int main()
{
	cin>>n;
	fo(i,1,n)
	{
		scanf("%lld",&a[i]);
		cnt[i]=++c1[a[i]];
		while(le[a[i]]>1&&fd(d[a[i]][le[a[i]]-2],d[a[i]][le[a[i]]-1])<=fd(d[a[i]][le[a[i]]-1],i))
		{
			le[a[i]]--,d[a[i]].pop_back();
		}
		++le[a[i]];
		d[a[i]].push_back(i);
		int x=d[a[i]][le[a[i]]-1],y;
		while(le[a[i]]>1)
		{
			y=d[a[i]][le[a[i]]-2];
			if(get(x,cnt[i])<=get(y,cnt[i])) le[a[i]]--,d[a[i]].pop_back();
			else break;
			x=y;
		}
		f[i]=get(x,cnt[i]);
	}
	printf("%lld\n",f[n]);
}

猜你喜欢

转载自blog.csdn.net/hzj1054689699/article/details/83578503
今日推荐