百折不挠|

Xdik

园龄:1个月粉丝:7关注:24

P8726

首先很显然可以得出 O(n2) 的暴力dp: dpi=max{dpj2+ti×tjfj} 显然过不了,考虑优化,因为式子里面有 ti×tj 这一项,故排除单调队列优化,线段树优化,所以考虑斜率优化,发现根本不用推式子,在这里面 ti 是自变量,dpi 是因变量,斜率就是 tj ,纵截距就是 dpj2fj 然后就可以将这个式子看成一个一次函数了,那么怎么解决在前面这么多函数中 xti 的最大值呢,用李超线段树就行了不会李超线段树的戳这,可以去看看题解

O(n2) 的暴力代码:


#include <bits/stdc++.h>
#define int long long
#pragma GCC optimeze(2)
using namespace std;
const int N = 5e5 + 5;
long long read() {
    long long flag = 1, x = 0;
    char ch = 0;
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            flag = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    return x * flag;
}
int dp[N], n, t[N], f[N],  ans = 0;
signed main() {
    n = read();
    for (int i = 1; i <= n; i++) {
        t[i] = read();
    }
    for (int i = 1; i <= n; i++) {
        f[i] = read();
    }
    memset(dp, 0xc0, sizeof dp);
    dp[1] = 0;
    for (int i = 2; i <= n; i++) {
        for (int j = 1; j < i; j++) {
             dp[i] = max(dp[i], dp[j] / 2 + t[i] * t[j] - f[j]);
         }
         ans = max(ans, dp[i]);
     }
    cout << ans;
    return 0;
}

AC Code:

#include <bits/stdc++.h>
#pragma GCC optimeze(2)
#pragma GCC optimeze(3)
#define int long long
#define PII pair<int, int>
using namespace std;
const double eps=1e-9;
const int N=5e5+5; 
const int V=2e4;//题面中t的值域,也就是所有一次函数x的取值范围
int read(){
    int x=0, flag=1;
    char ch=0;
    while (ch<'0'||ch>'9'){
        if (ch=='-')
            flag=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') {
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*flag;
}	
struct lcxds{
	int l,r;
	int v;
}tr[V*4];
struct line{
	double k,b;
}l[N];
int q,n,t[N],f[N],dp[N];
double c(double a,int bh){
	return a*l[bh].k+l[bh].b;
}
bool pd(double x,int i,int j){
	if(c(x,i)-c(x,j)>eps)return 1;//double有精度误差,所以用了一个eps
	if(c(x,j)-c(x,i)>eps)return 0;
}
void change(int l,int r,int p,int s,int t,int i){
	if(s>=l&&t<=r){
		if(pd(s,i,tr[p].v)&&pd(t,i,tr[p].v)){
			tr[p].v=i;
			return;
		}
		int m=(s+t)>>1;
		if(pd(m,i,tr[p].v))swap(i,tr[p].v);
		if(pd(s,i,tr[p].v))change(l,r,p*2,s,m,i);
		if(pd(t,i,tr[p].v))change(l,r,p*2+1,m+1,t,i);
		return;
	}
	int m=(s+t)>>1;
	if(m>=l)change(l,r,p*2,s,m,i);
	if(m<r)change(l,r,p*2+1,m+1,t,i);
}
int query(double k,int p,int s,int t){
	if(s==t)return tr[p].v;
	int m=(s+t)>>1,ans1=0,ans=tr[p].v;
	if(m>=k)ans1=query(k,p*2,s,m);
	else ans1=query(k,p*2+1,m+1,t);
	if(pd(k,ans1,ans))ans=ans1;
	return ans;
}//以上两个函数就是李超线段树的实现
signed main() {
	n=read();
	for(int i=1;i<=n;i++){
		t[i]=read();
	}
	for(int i=1;i<=n;i++){
		f[i]=read();
	}
	memset(dp,0xc0,sizeof dp);
	dp[1]=0;
	l[1].k=t[1],l[1].b=dp[1]/2-f[1];
	change(1,V,1,1,V,1);//先将1这根直线插进去
	int ans=0;
	for(int i=2;i<=n;i++){
		int j=query(t[i],1,1,V);//找到使值最大的那一条直线
		j=max(j,1ll);//这里要和1取一个max,避免选到0这根直线
		dp[i]=dp[j]/2+t[i]*t[j]-f[j];
		l[i].k=t[i],l[i].b=dp[i]/2-f[i];
		change(1,V,1,1,V,i);//插入直线
		ans=max(ans,dp[i]);
	}
	cout<<ans;
    return 0;
}

本文作者:Xdik

本文链接:https://www.cnblogs.com/Xdik/p/18708134

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Xdik  阅读(4)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起