zoukankan      html  css  js  c++  java
  • 【CF321E】+【bzoj5311】贞鱼

    决策单调性 + WQS二分

    我们首先列出转移式: (f[i]=Min(f[j]+Sum[j+1 , i]))

    首先我们考虑如果让一段区间的小鱼在一起的代价怎么预处理,我们可以对于一个上三角矩阵求个二维前缀和,那么我们计算 ([j+1,i]) 这段区间的代价就是 (S[i,i]-S[i,j]) ,得到的是一个等腰直角三角形的和

    那么原来的转移式就可以这么写:(f[i]=Min(f[j]+S[i,i]-S[i,j]))

    然后我们不考虑车辆数量的限制,那么对于 j 和 k ,如果 (j<k) ,那么首先 (f[j]) 必然小于 (f[k]) ,但是随着 i 的增大, (S[i,i]) 是不变的,但是 (S[i,j])(S[i,k]) 之间的差肯定是越来越大的,即对于 (f[j]-S[i,j])(f[k]-S[i,k]) ,肯定是一开始选 j 比较优,后来两个函数有一个交点,过了这个交点就是 k 比较优了,并且两个函数的交点只有一个,所以我们可以二分一个交点,然后保持交点和 j 同时单调递增,这样就可以做一个 O(n) 的斜率优化 dp 了...

    但问题是这 goushi 的车辆是有数量限制的,但是我们发现总价格和使用车辆数是负相关的,那么我们可以考虑对于每辆车加上一个租赁代价,这在斜率优化中是不影响计算的,然后租赁的代价越高,最优解中使用车辆数肯定越小,那么我们可以二分这个租赁代价,得到车辆数恰好为指定的 K 时,把租赁的代价减去,这样就得到了答案

    总复杂度讲道理是 (O(n~log n ~ log ~S[n,n])) ,因为外面 wqs 二分 (S[n,n]),里面也要二分交点

    code

    //by Judge
    #pragma GCC optimize("Ofast")
    #include<bits/stdc++.h>
    #define Rg register
    #define fp(i,a,b) for(Rg int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(Rg int i=(a),I=(b)-1;i>I;--i)
    #define ll long long
    using namespace std;
    const int M=4003;
    #ifndef Judge
    #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    #endif
    char buf[1<<21],*p1=buf,*p2=buf;
    inline ll read(){ ll x=0,f=1; char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
    } int n,K,ans,s[M][M],f[M],w[M];
    inline int calc(int j,int i){
    	return f[j]+s[i][i]-s[j][i];
    }
    inline bool judge(int j,int k,int i){ //判断 f[i] 大小 
    	int valj=calc(j,i),valk=calc(k,i);
    	if(valj^valk) return valj>valk;
    	return w[j]>=w[k];
    }
    inline int rate(int j,int k){  //得到交点位置 
    	int l=k+1,r=n;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		if(judge(j,k,mid)) r=mid-1;
    		else l=mid+1;
    	} return l;
    }
    inline bool check(int mid){ //二分附加权值 
    	static int head,tail,q[M];
    	q[head=tail=1]=0;
    	fp(i,1,n){ //斜率优化 
    		while(head<tail&&judge(q[head],q[head+1],i)) ++head;
    		f[i]=calc(q[head],i)+mid,w[i]=w[q[head]]+1;
    		while(head<tail&&rate(q[tail-1],q[tail])>rate(q[tail],i)) --tail; q[++tail]=i;
    	} return w[n]<=K;
    }
    int main(){ n=read(),K=read();
    	fp(i,1,n) fp(j,1,n) s[i][j]=read();
    	fp(i,1,n) fp(j,1,i) s[i][j]=0;
    	fp(i,1,n) fp(j,1,n) s[i][j]=s[i][j-1]+s[i][j];
    	fp(i,1,n) fp(j,1,n) s[i][j]=s[i-1][j]+s[i][j];
    	int l=0,r=s[n][n];
    	while(l<=r){ int mid=(l+r)>>1;
    		if(check(mid)) r=mid-1,ans=f[n]-K*mid;
    		else l=mid+1;
    	} return !printf("%d
    ",ans);
    }
    
  • 相关阅读:
    什么是用户画像——从零开始搭建实时用户画像(一)
    一站式Kafka平台解决方案——KafkaCenter
    Druid 0.17入门(4)—— 数据查询方式大全
    流媒体与实时计算,Netflix公司Druid应用实践
    解读银行卡支付背后的原理
    求求你了,不要再自己实现这些逻辑了,开源工具类不香吗?
    编程坑太多,Map 集合怎么也有这么多坑?一不小心又踩了好几个!
    设计数据库 ER 图太麻烦?不妨试试这两款工具,自动生成数据库 ER 图!!!
    一口气带你踩完五个 List 的大坑,真的是处处坑啊!
    轻轻一扫,立刻扣款,付款码背后的原理你不想知道吗?|原创
  • 原文地址:https://www.cnblogs.com/Judge/p/11311660.html
Copyright © 2011-2022 走看看