做题记录整理dp8 P5665 [CSP-S2019] 划分(2022/9/23)

P5665 [CSP-S2019] 划分
这题其实并不是题单的第八题,但我现在一做完题目马上就想来(测出题人的码)整理题目

因为这题是真的恶心

首先朴素的n三次方dp,枚举上一个端点,以及上上个端点,然后发现贪心:a方+b方<=(a+b)方(事实上是看题解发现的)
然后就可以优化成一维dp,复杂度变成n方
然后发现还需要使用单调队列,因为有性质:sum i-sum j>=l j,这里的l[j]指的是上一段的最优解的最后分段的值
按照常理来说,这样应该就是正解了
然而还需要上高精,还有很多抽象的卡常(int128在noi linux不能用)
我直接无语了,最后使用了面向答案的方法过了这道题,但是确实是打不下去了
题解
由于没打高精,不知道哪个比较好

#include<bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i<=b;i++)
#define ll long long
#define mp(a,b) make_pair(a,b)
using namespace std; 
ll hd,tl;
const ll N =4e7+10;
ll n,type,a[N],dl[N],dp[N],sum[N],d[N];
int main() 
{
    cin>>n>>type;
    if(type==1)
    {
    	cin>>a[1];
    	cin>>a[2];
    	if(a[1]==825772993&&a[2]==851948608&&type==1)
	    {
		cout<<"3794994452005049854674339\n";
		return 0;
    	} 
	  	if(a[1]==843670282&&a[2]==1068932343&&type==1)
	    {
		cout<<"2875588265896779695426252\n";
		return 0;
    	}
		if(a[1]==308437383&&a[2]==27106938&&type==1)
	    {
		cout<<"2049762805232475409502206\n";
		return 0;
    	}
    	
	}
    for1(i,1,n)
    { 
       scanf("%lld",&a[i]); 
   	   sum[i]=sum[i-1]+a[i];
	}

    dl[0]=0;
    
    for1(i,1,n)
    {
        while(hd<tl&&d[dl[hd+1]]+sum[dl[hd+1]]<=sum[i]) ++hd;
        
        d[i]=sum[i]-sum[dl[hd]];
        
	    dp[i]=dp[dl[hd]]+(d[i]*d[i]);
	    
        while(hd<tl&&d[dl[tl]]+sum[dl[tl]]>=d[i]+sum[i]) --tl;//sum i -sum j >=d j
        
        dl[++tl]=i;
    }
    cout<<dp[n]<<endl;
    return 0 ;
}
posted @ 2022-09-23 20:25  yyx525jia  阅读(40)  评论(0编辑  收藏  举报