• CDQ分治套斜率优化


    CDQ分治套斜率优化

    有一些特殊的动态规划题目,貌似可以化作(frac{g_j-g_k}{g'_j-g'_k}<K_i)的斜率不等式,但实际上,(g'_j)以及(g'_k)却没有单调性,所以无法用单调队列/二分搜索单调栈实现斜率优化。于是,便有巨佬便想出了CDQ分治套斜率优化的方法。

    如何CDQ?

    对子状态按照(K)从小到大排序,分治,递归前,将子序列按照子状态的原下标分成两个子序列,子序列内部保持之前排序的顺序不变;

    递归到子序列长度为(1)时,像普通斜率优化一样建点;

    回溯时,将左子序列中的状态插入单调队列,像普通斜率优化一样对右子序列中的状态更新。

    这样的话,当递归到底层时,当前子状态已经被所有原下标在自己之前的状态更新了。

    例题: [SDOI2012]任务安排

    代码实现:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #define int long long
    #define maxn 300000
    #define INF 0x3f3f3f3f3f3f3f3f
    using namespace std;
    void read(int &x){
    	int f=1;x=0;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	x*=f;
    }
    int A[maxn+5],B[maxn+5],F[maxn+5];
    struct vec{
    	int i,x,y;vec(){i=x=y=0;}
    	vec(int x,int y){i=0,this->x=x,this->y=y;}
    	vec(int i,int x,int y){this->i=i,this->x=x,this->y=y;}
    	friend vec operator-(vec a,vec b){return vec(a.x-b.x,a.y-b.y);}
    	friend bool operator<(vec a,vec b){if(a.x==0) return 0;return 1.0*a.y/a.x<1.0*b.y/b.x;}
    } s[maxn+5],q[maxn+5],t[maxn+5];
    int head=1,tail=0;
    void push(vec x){
    	while(tail>head&&(x-q[tail-1])<(q[tail]-q[tail-1])) tail--;
    	q[++tail]=x;
    }
    void pop(int k){
    	while(tail>head&&(q[head+1]-q[head])<vec(1,k)) head++;
    }
    int n,ss;
    void CDQ(int left,int right){
    	if(left==right){s[left].x=B[left],s[left].y=F[left];return;}
    	int mid=(left+right)/2,x1=left-1,x2=mid;
    	for(int i=left;i<=right;i++)
    		s[i].i<=mid?t[++x1]=s[i]:t[++x2]=s[i];
    	for(int i=left;i<=right;i++) s[i]=t[i];
    	CDQ(left,mid),head=1,tail=0;
    	for(int i=left;i<=mid;i++) push(s[i]);
    	for(int i=mid+1;i<=right;i++){
    		pop(ss+A[s[i].i]);
    		int j=q[head].i;
    		F[s[i].i]=min(F[s[i].i],F[j]+ss*(B[n]-B[j])+A[s[i].i]*(B[s[i].i]-B[j]));
    	}
    	CDQ(mid+1,right),x1=left,x2=mid+1;
    	for(int i=left;i<=right;i++)
    		if(x1<=mid&&(x2>right||s[x1].x<s[x2].x||(s[x1].x==s[x2].x&&s[x1].y<s[x2].y))) t[i]=s[x1++];
    		else t[i]=s[x2++];
    	for(int i=left;i<=right;i++) s[i]=t[i];
    }
    bool cmp(vec a,vec b){return A[a.i]<A[b.i];}
    #undef int
    int main(){
    #define int long long
    	read(n),read(ss);
    	for(int i=1;i<=n;i++) read(A[i]),read(B[i]),A[i]+=A[i-1],B[i]+=B[i-1],s[i].i=i;
    	memset(F,0x3f,sizeof(F));
    	F[0]=0,sort(s+1,s+n+1,cmp);
    	CDQ(0,n);
    	printf("%lld
    ",F[n]);
    }
    
  • 相关阅读:
    Spark_总结五
    Linux sudo 命令使用简介
    Linux nohup命令应用简介--让Linux的进程不受终端影响
    Linux 修改linux的SSH的默认端口
    Linux 多个vi、vim进程编辑同一文件时的临时文件问题
    Linux 系统下用源码包安装软件
    Linux 配置iso系统盘为本地yum源
    profile,bashrc,.bash_profile,.bash_login,.profile,.bashrc,.bash_logout浅析 Part 2
    Linux profile1,bashrc,.bash_profile,.bash_login,.profile,.bashrc,.bash_logout浅析 Part1
    Linux 目录结构学习与简析 Part2
  • 原文地址:https://www.cnblogs.com/OIER-Yu/p/11440027.html
Copyright © 2020-2023  润新知