斜率优化

HDU3507 Print Article

\(dp[i]\) 表示 \(1\sim i\) 的方式,转移就是同一段或者多一个,令 \(s[i]=\sum_{j=1}^i a[j]\)

\[dp[i]=\min_{j=1\sim i-1}\{dp[j]+(s[i]-s[j])^2+M\}\\\\ (2\cdot s[i]\cdot s[j])+(dp[i]-s[i]^2-M)=dp[j]+s[j]^2 \]

……被多测打败了 /ll

Code
//Author: RingweEH
int X( int j ) { return s[j]; }
int Y( int j ) { return dp[j]+s[j]*s[j]; }
db slope( int t1,int t2 ) 
{ return (X(t2)!=X(t1)) ? (db)(Y(t2)-Y(t1))/(X(t2)-X(t1)) : LLONG_MAX; }

signed main()
{
//freopen( "exam.in","r",stdin );

    while ( scanf("%lld%lld",&n,&m)!=EOF )
    {
        for ( int i=1; i<=n; i++ ) s[i]=s[i-1]+read();

        head=tail=0; q[++tail]=0;
        for ( int i=1; i<=n; i++ )
        {
            while ( head<tail && slope(q[head],q[head+1])<=2*s[i] ) head++;
            dp[i]=dp[q[head]]+(s[i]-s[q[head]])*(s[i]-s[q[head]])+m;
            while ( head<tail && slope(q[tail-1],q[tail])>=slope(q[tail-1],i) ) tail--;
            q[++tail]=i;
        }

        printf("%lld\n",dp[n] );
        s[0]=0; memset( dp,0,sizeof(int)*n );
    }

    return 0;
}

LOJ 10188 玩具装箱

\[dp[i]=S[i]^2-2\cdot S[i]\cdot L+dp[j]+(S[j]+L)^2-2\cdot S[i]\cdot S[j]\\\\ 2(S[i])S[j]+(dp[i]-S[i]^2+2S[i]L)=(dp[j]+(S[j]+L)^2) \]

Code

Code

LOJ 10192 锯木厂选址

\(dp[i]\) 表示将第二个锯木厂放在 \(i\) 的最小费用。假设一开始全部移到了本来就有的那个厂,相当于用总费用换一部分到 \(j\) (枚举的第一个),再换一部分到 \(i\) 。对 \(d[i]\) 做后缀和,\(w[i]\) 做前缀和,把第一部分放到 \(j\) 就是少了 \(w[j]\times d[j]\) ;第二部分是 \((w[i]-w[j])\times d[i]\) 。那么式子就是

\[dp[i]=tot−d[j]\cdot w[j]−d[i]\cdot w[i]+d[i]\cdot w[j]\\\\ d[i]\cdot w[j]-(dp[i]+d[i]\cdot w[i]-tot)=d[j]\cdot w[j] \]

式子 \(w\) 抄成 \(d\) 了调一年 /fn

Code

Code

LOJ10189 仓库建设

\(sp[i]=\sum_{j=1}^i p[j],s[i]=\sum_{j=1}^i p[j]\times x[j]\) ,那么有

\[dp[i]=dp[j]+x[i]\times(sp[i]-sp[j])-(s[i]-s[j])+c[i]\\\\ dp[j]+s[j]=x[i]\times sp[j]+(dp[i]+s[i]-c[i]-x[i]\times sp[i]) \]

Code

Code

P2900 Land Acquisition G

考虑一块土地该怎么算。显然,对于一块地 \(x\) ,如果存在一个 \(y\) 的长宽均大于它,那么这块土地跟没有一样,排序加单调栈解决。

假设是按照长排序,那么此时留下的土地宽降序。显然在最优决策下,每组土地是连续的一段。方程就是

\[dp[i]=\min(dp[i],dp[j]+w[j+1]\times l[i])\\\\ dp[j]=-w[j+1]\times l[i]+dp[i] \]

注意下那个重载的写法,PKUSCD1T1 就这么没得。

Code
//Author: RingweEH
const int N=2e5+10;
struct StrLand
{
	int x,y;
	bool operator < ( const StrLand tmp ) const { return (x^tmp.x) ? x<tmp.x : y<tmp.y; }
}a[N],sta[N];
int n,top=0,q[N],head,tail,dp[N];

void Init()
{
	sort( a+1,a+1+n ); sta[++top]=a[1];
	for ( int i=2; i<=n; i++ )
	{
		while ( top && sta[top].y<=a[i].y ) top--;
		sta[++top]=a[i];
	}
	n=top;
}

//K=l[i] X=-w[j+1] Y=dp[j]
int X( int j ) { return -sta[j+1].y; }
int Y( int j ) { return dp[j]; }
db slope( int i,int j ) { return (db)(Y(j)-Y(i))/(X(j)-X(i)); }

signed main()
{
//freopen( "exam.in","r",stdin );

	n=read();
	for ( int i=1; i<=n; i++ ) a[i].x=read(),a[i].y=read();

	Init(); head=tail=1; q[1]=0;
	for ( int i=1,j; i<=n; i++ )
	{
		while ( head<tail && slope(q[head],q[head+1])<=1.0*sta[i].x ) head++;
		j=q[head]; dp[i]=dp[j]+sta[j+1].y*sta[i].x;
		while ( head<tail && slope(q[tail-1],q[tail])>=slope(q[tail],i) ) tail--;
		q[++tail]=i;
	}

	printf("%lld\n",dp[n] );

	return 0;
}

LOJ10190 特别行动队

\(s[i]=\sum_{j=1}^i x[j]\)

\[dp[i]=dp[j]+a\cdot (s[i]-s[j])^2+b\cdot (s[i]-s[j])+c\\\\ dp[j]+a\cdot s[j]^2-b\cdot s[j]=2a\cdot s[i]s[j]+(dp[i]-a\cdot s[i]^2-b\cdot s[i]+c) \]

Code

Code

UOJ104 Split the sequence

\(s[i]=\sum_{j=1}^i a[j]\) ,有简单DP:

\[dp[i][k]=\min(dp[j][k-1]+s[j]\times(s[i]-s[j])\\\\ \]

注意到这个式子只和 \(dp[][k-1]\) 有关,所以可以跑 \(K\) 次斜率优化。

\[f[i]=\min(g[j]+s[j]\times(s[i]-s[j]))\\\\ g[j]-s[j]^2=-s[i]\times s[j]+f[i] \]

Code
//Author: RingweEH
//K=-s[i] X=s[j] Y=g[j]-s[j]^2
const int N=1e5+10,INF=1e18;
int n,k,s[N],f[N],g[N],head,tail,q[N],path[N][210];

int X( int j ) { return s[j]; }
int Y( int j ) { return g[j]-s[j]*s[j]; }
db slope( int i,int j ) 
{ return (X(j)^X(i)) ? (db)(Y(j)-Y(i))/(X(j)-X(i)) : INF; }

signed main()
{
//freopen( "exam.in","r",stdin );

	n=read(); k=read();
	for ( int i=1; i<=n; i++ ) s[i]=s[i-1]+read();

	for ( int cas=1; cas<=k; cas++ )
	{
		head=tail=0; q[0]=0;
		for ( int i=1; i<=n; i++ )
		{
			while ( head<tail && slope(q[head],q[head+1])>=-s[i] ) head++;
			int j=q[head]; f[i]=g[j]+s[j]*(s[i]-s[j]); path[i][cas]=j;
			while ( head<tail && slope(q[tail-1],q[tail])<=slope(q[tail],i) ) tail--;
			q[++tail]=i;
		}
		for ( int i=1; i<=n; i++ ) g[i]=f[i],f[i]=0;
	}

	printf("%lld\n",g[n] ); int nw=n;
	for ( int i=k; i>=1; i-- ) printf("%lld ",path[nw][i] ),nw=path[nw][i];

	return 0;
}

BZOJ3437 小P的牧场

这真的不是仓库建设简化版……?/xia

hoho!改了改输入过了 /px

Code
//Author: RingweEH
int X( int j ) { return sp[j]; }
int Y( int j ) { return dp[j]+s[j]; }
db slope( int t1,int t2 ) { return (db)(Y(t2)-Y(t1))/(X(t2)-X(t1)); }

signed main()
{
//freopen( "exam.in","r",stdin );

	n=read();
	for ( int i=1; i<=n; i++ ) x[i]=i-1;
	for ( int i=1; i<=n; i++ ) c[i]=read();
	for ( int i=1; i<=n; i++ ) p[i]=read();

	sp[0]=s[0]=0;
	for ( int i=1; i<=n; i++ ) sp[i]=sp[i-1]+p[i],s[i]=s[i-1]+p[i]*x[i];
	head=tail=1,q[1]=0;
	for ( int i=1,j; i<=n; i++ )
	{
		while ( head<tail && slope(q[head],q[head+1])<=x[i] ) head++; j=q[head];
		dp[i]=dp[j]+x[i]*(sp[i]-sp[j])-(s[i]-s[j])+c[i];
		while ( head<tail && slope(q[tail-1],q[tail])>=slope(q[tail],i) ) tail--;
		q[++tail]=i;
	}

	printf("%lld\n",dp[n] );

    return 0;
}

LOJ2035 征途

方差 \(s^2=\dfrac{\sum (x-X)^2}{m}\) ,所以

\[\begin{aligned} m^2s^2&=m\sum(x-X)^2=m\sum x^2-2mX\sum x+m^2X^2\\\\ &=m\sum x^2-m^2X^2 \end{aligned} \]

也就是说最小化 \(\sum x^2\) 即可。设 \(dp[i][k]\) 表示 \(1\sim i\)\(k\) 天走完的最小代价。

\[dp[i][k]=\min(dp[j][k-1]+(s[i]-s[j])^2)\\\\ f[i]=\min(g[j]+(s[i]-s[j])^2)\\\\ g[j]+s[j]^2=2s[i]s[j]+(f[i]-s[i]^2) \]

K=2s[i] X=s[j] Y=g[j]+s[j]^2

和之前一样也是转移只和 \(k\) 相关的 DP ,直接暴力跑 \(m\) 次,\(O(nm)\) 的复杂度可以接受。

ps:题目长得很像扶苏那道忘情,大概WQS还能更优一点。

Code

Code

posted @ 2021-05-20 20:23  MontesquieuE  阅读(146)  评论(0)    收藏  举报