[BZOJ4700]适者(CDQ分治+DP/李超线段树)
如果没有秒杀,就是经典的国王游戏问题,按t/a从小到大排序即可。
考虑删除两个数i<j能给答案减少的贡献:S[i]*T[i]+P[i-1]*A[i]-A[i]+S[j]*T[j]+P[j-1]*A[j]-A[j]-T[i]*A[j]
其中P为T=(D-1)/ATK+1的前缀和,S为A的后缀和。
我们设bi=S[i]*T[i]+P[i-1]*A[i]-A[i],考虑当i固定时,j可能取什么值。
解法一:CDQ分治
不难发现,当i<j<k时k比j优的充要条件是b[k]-T[i]*A[k]>b[j]-T[i]*A[j],即T[i]<(b[k]-b[j])/(A[k]-A[j])。
不难看出斜率优化的模型,最后要最大化的是b[k]-T[i]*A[k]即所有点(A[k],b[k])以-T[i]的斜率投影到y轴上的最高点,于是答案一定在上凸壳上。
注意到这里点的横坐标A和询问斜率T均不单调,需要CDQ分治。
每次分治左半边按T排序,右半边按A排序并将凸包建好,然后左端点从前往后扫即可。
#include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
int n,m,q[N];
ll A,B,ans,tot,c[N];
struct P{ int x,y; ll z; }a[N],b[N]; bool cmp(const P &a,const P &b){ return a.x*b.y>b.x*a.y; }
bool cmp2(const P &a,const P &b){ return a.y>b.y; }
bool cmp3(const P &a,const P &b){ return a.x<b.x; }
ll calc(int x,int y){ return tot-a[x].z-a[y].z+a[y].x*a[x].y; }
double sl(int x,int y){ return (double)(a[y].z-a[x].z)/(a[y].x-a[x].x); } void solve(int l,int r){
if (l>=r) return;
int mid=(l+r)>>;
solve(l,mid); solve(mid+,r);
sort(a+l,a+mid+,cmp2); sort(a+mid+,a+r+,cmp3);
int st=,ed=;
rep(i,mid+,r){
while (st<ed && sl(q[ed-],q[ed])<sl(q[ed],i)) ed--;
q[++ed]=i;
}
rep(i,l,mid){
while (st<ed && sl(q[st],q[st+])>a[i].y) st++;
ans=min(ans,calc(i,q[st]));
}
} int main(){
freopen("bzoj4700.in","r",stdin);
freopen("bzoj4700.out","w",stdout);
scanf("%d%d",&n,&m);
rep(i,,n) scanf("%d%d",&a[i].x,&a[i].y),A+=a[i].x,a[i].y=(a[i].y-)/m+;
sort(a+,a+n+,cmp);
rep(i,,n){
A-=a[i].x; B+=a[i].y;
a[i].z=a[i].x*(B-)+A*(a[i].y);
tot+=a[i].x*(B-);
}
ans=tot; solve(,n); printf("%lld\n",ans);
return ;
}
解法二:李超树
不难发现,i固定时,j对答案减小的贡献是b[j]-T[i]*A[j],这个式子可以理解为直线y=-A[j]*x+b[j]在T[i]处的总坐标。
问题转化为,i从大到小枚举,需要支持插入一条直线与在线查询某个横坐标上所有直线总坐标最大值。
动态维护凸包问题,显然用李超树。
#include<cstdio>
#include<algorithm>
#define ls (x<<1)
#define rs (ls|1)
#define lson ls,L,mid
#define rson rs,mid+1,R
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
int n,m,v[N<<];
ll tot,ans,res,L[N],S[N],k[N],b[N];
struct P{ int a,t; }p[N];
bool operator <(const P &a,const P &b){ return a.a*b.t>b.a*a.t; }
bool pd(int x,int y,int p){ return k[x]*p+b[x]>k[y]*p+b[y]; }
ll calc(int x,int p){ return k[x]*p+b[x]; } void ins(int x,int L,int R,int p){
if (L==R){ if (pd(p,v[x],L)) v[x]=p; return; }
int mid=(L+R)>>;
if (k[p]>k[v[x]]){
if (pd(p,v[x],mid)) ins(lson,v[x]),v[x]=p; else ins(rson,p);
}else{
if (pd(p,v[x],mid)) ins(rson,v[x]),v[x]=p; else ins(lson,p);
}
} void que(int x,int L,int R,int k){
res=max(res,calc(v[x],k));
if (L==R) return;
int mid=(L+R)>>;
if (k<=mid) que(lson,k); else que(rson,k);
} int main(){
freopen("bzoj4700.in","r",stdin);
freopen("bzoj4700.out","w",stdout);
scanf("%d%d",&n,&m);
rep(i,,n) scanf("%d%d",&p[i].a,&p[i].t),p[i].t=(p[i].t-)/m+;
sort(p+,p+n+);
rep(i,,n) L[i]=L[i-]+p[i].t;
for (int i=n; i; i--) S[i]=S[i+]+p[i].a;
rep(i,,n) k[i]=-p[i].a,b[i]=S[i]*p[i].t+L[i-]*p[i].a-p[i].a,tot+=p[i].t*S[i]-p[i].a;
ins(,,n,n);
for (int i=n-; i; i--)
res=,que(,,n,p[i].t),ans=max(ans,b[i]+res),ins(,,n,i);
printf("%lld\n",tot-ans);
return ;
}
[BZOJ4700]适者(CDQ分治+DP/李超线段树)的更多相关文章
- Codechef TSUM2 Sum on Tree 点分治、李超线段树
传送门 点分治模板题都不会迟早要完 发现这道题需要统计所有路径的信息,考虑点分治统计路径信息. 点分治之后,因为路径是有向的,所以对于每一条路径都有向上和向下的两种.那么如果一条向上的路径,点数为\( ...
- cdqz2017-test10-rehearsal(CDQ分治&可持久化线段树&单调栈)
题意: 给出n个三元组 e[i]=(si,ti,wi) 第i个三元组的价值为 Σ w[j] ,j 满足以下4个条件: 1.j<i 2.tj<ti 3.sj<si 4.不存在j< ...
- Codeforces 1175G Yet Another Partiton Problem [DP,李超线段树]
Codeforces 思路 首先吐槽一句:partiton是个什么东西?我好像在百度翻译里面搜不到呀qwq 发现不了什么性质,那就直接上DP吧.注意到DP可以分层,所以设\(dp_i\)表示当前层,分 ...
- [CF1303G] Sum of Prefix Sums - 点分治,李超线段树
给定一棵 \(n\) 个点的带点权的树,求树上的路径 \(x_1,...,x_k\) ,最大化 \(\sum_{i=1}^k ia_{x_i}\) Solution 树上路径问题可用点分治. 考虑如何 ...
- Codeforces 1303G - Sum of Prefix Sums(李超线段树+点分治)
Codeforces 题面传送门 & 洛谷题面传送门 个人感觉这题称不上毒瘤. 首先看到选一条路径之类的字眼可以轻松想到点分治,也就是我们每次取原树的重心 \(r\) 并将路径分为经过重心和不 ...
- luogu4849 寻找宝藏 (cdq分治+dp)
设f[i]是已经走到i号点的值. 先要给第四维离散化.然后去重 第一维排序,第二维cdq分治,第三维cdq分治,第四维树状数组,找到满足j(x,y,z,w)<=i(x,y,z,w)的j,给i统计 ...
- Codeforces 1175G - Yet Another Partiton Problem(李超线段树)
Codeforces 题面传送门 & 洛谷题面传送门 这是一道李超线段树的毒瘤题. 首先我们可以想到一个非常 trivial 的 DP:\(dp_{i,j}\) 表示前 \(i\) 个数划 ...
- Codeforces Round #463 F. Escape Through Leaf (李超线段树合并)
听说正解是啥 set启发式合并+维护凸包+二分 根本不会啊 , 只会 李超线段树合并 啦 ... 题意 给你一颗有 \(n\) 个点的树 , 每个节点有两个权值 \(a_i, b_i\) . 从 \( ...
- 【BZOJ-4515】游戏 李超线段树 + 树链剖分 + 半平面交
4515: [Sdoi2016]游戏 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 304 Solved: 129[Submit][Status][ ...
随机推荐
- 82.Linux之VMware10.0.4_x64安装
一直想写linux前期软件的一些安装配置的博客,因为中途去弄CORDIC算法了,今天上午刚弄好,除法,乘累加,三角函数等都能达到要求,所以现在来写这块的博客,CORDIC博客就不写了,因为网上很多.V ...
- 我看到的最棒的Twisted入门教程!
http://www.douban.com/note/232204441/ http://www.cnblogs.com/sevenyuan/archive/2010/11/18/1880681.ht ...
- 如何扎实自己的Java基础?
问:如何扎实自己的Java基础? 答:玩好JDK JDK其实就是Java SE Development Kit的缩写,要玩好这东西可不简单.JDK主要包含了三部分,第一部分就是Java运行时环境,这其 ...
- 如何成为技术大牛——阿里CodeLife
天天写业务代码的程序员,怎么成为技术大牛,开始写技术代码? 几个误区 跟着大牛,就可以成为大牛.首先,大牛时间很宝贵,不可能花很多时间去指导你:其次,简单的模仿大牛,只能学到表面知识,不可能成为大牛: ...
- day04作业
1.for(初始化表达式:条件表达式:循环后的操作表达式){ 循环体: } class Test_Sum { public static void main(String[] args) { int ...
- Codeforces 332B Maximum Absurdity(DP+前缀和处理)
题目链接:http://codeforces.com/problemset/problem/332/B 题目大意:给你n个数和一个整数k,要求找到不相交的两个长度为k的区间,使得区间和最大,输出这两个 ...
- sql查询与修改数据库逻辑文件名,移动数据库存储路径
USE mydb GO --1.查询当前数据库的逻辑文件名 ) ) AS 'File Name 2'; --或通过以下语句查询: --SELECT name FROM sys.database_fil ...
- 【TensorFlow】一文弄懂CNN中的padding参数
在深度学习的图像识别领域中,我们经常使用卷积神经网络CNN来对图像进行特征提取,当我们使用TensorFlow搭建自己的CNN时,一般会使用TensorFlow中的卷积函数和池化函数来对图像进行卷积和 ...
- Oracle与Sqlserver数据共享
需求:在一个集成平台中有一个主系统使用的是Oralce数据库,子系统使用的SqlServer 数据库,如何让子系统的数据库与主系统的人员同步呢? 思路:通过服务WebService 公开接口 1.与主 ...
- 一行代码实现Okhttp,Retrofit,Glide下载上传进度监听
https://mp.weixin.qq.com/s/bopDUFMB7EiK-MhLc3KDXQ essyan 鸿洋 2017-06-29 本文作者 本文由jessyan投稿. jessyan的博客 ...