HDU3045【Picnic Cows】

Picnic Cows

题目

HDU3045


解析

发现数据有些大(n<=400000),加上还有一个多测,出题人的目的昭然若揭:
线性或nlogn复杂度
考虑动态规划,设DPi为前i个的最优解,则显然
dpi=min(dpj+si−sj−aj+1∗(i−j)),1<=j<=i−t+1(s为a的前缀和)
仔细观察一下,发现存在万恶的*(i-j),不能单调队列,因为对于每个i,式子的值是不同的
这时候我们就要搬出一个万恶之源有用的东西
斜率优化
具体就是单调队列+凸壳维护
反正特别恶心就对了
最终式子为设断k比断j优,则有
dpj−dpk+sk−sj+j∗aj+1−k∗ak+1<=i∗(aj+1−ak+1)
感谢这个不会被Markdown掉的乘号
剩余见代码
算法上限为O(nlogn),瓶颈在于排序,当然排序可以O(n)(大雾
最后注意一下:
多测用scanfEOF判,开longlong

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
inline bool idigit(char x){
    
    return (x<'0'|x>'9')?0:1;}
inline int read()
{
    
    
	int num=0,f=1;
	char c=0;
	while(!idigit(c=getchar())){
    
    if(c=='-')f=-1;}
	while(idigit(c))num=(num<<1)+(num<<3)+(c&15),c=getchar();
	return num*f;
}
inline void write(int x)
{
    
    
	int F[20];
	int tmp=x>0?x:-x;
	if(x<0)putchar('-');
	int cnt=0;
	while(tmp>0){
    
    F[cnt++]=tmp%10+'0';tmp/=10;}
	while(cnt>0)putchar(F[--cnt]);
	if(x==0)putchar('0');
	putchar('\n');
}
int n,t,a[400010],s[400010],dp[400010],q[400010],l,r,tot;
inline int L(int x,int y){
    
    return a[x+1]-a[y+1];}
inline int R(int x,int y){
    
    return dp[x]-dp[y]-s[x]+s[y]+x*a[x+1]-y*a[y+1];}
inline int Dp(int x,int y){
    
    return dp[y]+s[x]-s[y]-a[y+1]*(x-y);}//灵魂式子
inline void DP()
{
    
    
	for(int i=1;i<=n;++i)
	{
    
    
		while(l<r&&R(q[l+1],q[l])<=i*L(q[l+1],q[l]))++l;
		dp[i]=Dp(i,q[l]),tot=i-t+1;
		if(tot<t)continue;
		while(l<r&&R(tot,q[r])*L(q[r],q[r-1])<=L(tot,q[r])*R(q[r],q[r-1]))--r;//维护
		q[++r]=tot;//单调队列
	}
	write(dp[n]);//DP结果
}
signed main()
{
    
    
	while(scanf("%lld",&n)!=EOF)
	{
    
    
		t=read(),dp[0]=s[0]=l=r=q[0]=0;//l和r为队列两端,q为队列数组,采用前闭后闭形式
		for(int i=1;i<=n;++i)a[i]=read(),s[i]=s[i-1]+a[i];
		sort(a+1,a+n+1);
		DP();//DP部分
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zhanglili1597895/article/details/114669104