博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
BZOJ 2402 陶陶的难题II (树链剖分、线段树、凸包、分数规划)
阅读量:5292 次
发布时间:2019-06-14

本文共 4463 字,大约阅读时间需要 14 分钟。

毒瘤,毒瘤,毒瘤……

\(30000\)这个数据范围,看上去就是要搞事的啊。。。

题目链接:

题解:

首先化式子: 假设二分的答案为\(mid\)\(\frac{y_i+q_j}{x_i+p_j}\ge mid, y_i+q_j\ge mid(x_i+p_j), y_i-mid\times x_i+q_j-mid\times p_j\ge 0\)

那么我们实际上就是要找一个\(i\)使得\(-x_i\times mid+y_i\)最大,同理找一个\(j\)使得\(-p_j\times mid+q_j\)最大,这两个任务完全一样,现在就讨论前者

为了方便(强迫症)让我们把它变成\(-a_ix+b_i\)的形式

仿照斜率优化的思路,如果\(a_i<a_j\),那么\(i\)不劣于\(j\)当且仅当\(-a_ix+b_i\ge -a_jx+b_j, x\ge \frac{b_j-b_i}{a_j-a_i}\).

那么假设有并排着的三个点\(i,j,k,a_i<a_j<a_k\), 则\(i\)不劣于\(j\)当且仅当\(x\ge \frac{b_j-b_i}{a_j-a_i}\), \(k\)不劣于\(j\)当且仅当\(x\le \frac{b_k-b_j}{a_k-a_j}\), 如果\(\frac{b_j-b_i}{a_j-a_i}\le \frac{b_k-b_j}{a_k-a_j}\), 那么对于任何\(x\)上面两个条件至少有一个成立,那么\(j\)就是无用的。也就是说有用的点一定满足斜率递减,在上凸壳的左半边上。

这题没有修改操作,那么我们可以用线段树求出每个子区间内的凸壳,然后对于每个询问,先分数规划二分答案,然后求树链剖分划分成的每一段区间的最大值,这个可以通过在线段树每个子区间的凸壳上二分得到。

时间复杂度\(O(n\log^4n)\)

(这可能是我最近写的几道题唯一一道跑进BZOJ前一半的?23333)

代码

#include
#include
#include
#include
#include
using namespace std;const int N = 3e4;const double EPS = 1e-8;const double INF = 1e6;int dcmp(double x) {return x<-EPS ? -1 : (x>EPS ? 1 : 0);}struct Point{ double x,y; Point() {} Point(double _x,double _y) {x = _x,y = _y;} bool operator <(const Point &arg) const {return dcmp(x-arg.x)<0 || (dcmp(x-arg.x)==0 && dcmp(y-arg.y)<0);}};struct Edge{ int v,w,nxt;} e[(N<<1)+3];int fe[N+3];int fa[N+3];int dfn[N+3];int idfn[N+3];int sz[N+3];int tpn[N+3];int dep[N+3];int hvs[N+3];double a1[N+3],b1[N+3],a2[N+3],b2[N+3];int n,q,en,cnt;struct SegmentTree{ vector
sgt[(N<<2)+3]; void CHpush(int id,Point x) { int j = sgt[id].size()-1; while(j>0 && dcmp((sgt[id][j].y-sgt[id][j-1].y)*(x.x-sgt[id][j].x)-(sgt[id][j].x-sgt[id][j-1].x)*(x.y-sgt[id][j].y))<=0) { sgt[id].pop_back(); j--; } sgt[id].push_back(x); } void build(int rtn,int le,int ri,double a[],double b[]) { if(le==ri) { sgt[rtn].push_back(Point(a[idfn[le]],b[idfn[le]])); return; } int mid = (le+ri)>>1; build(rtn<<1,le,mid,a,b); build(rtn<<1|1,mid+1,ri,a,b); int i = 0,j = 0; while(i
<<1].size() && j
<<1|1].size()) { if(sgt[rtn<<1][i]
<<1|1][j]) {CHpush(rtn,sgt[rtn<<1][i]); i++;} else {CHpush(rtn,sgt[rtn<<1|1][j]); j++;} } while(i
<<1].size()) {CHpush(rtn,sgt[rtn<<1][i]); i++;} while(j
<<1|1].size()) {CHpush(rtn,sgt[rtn<<1|1][j]); j++;} } double calc(int rtn,double x) { int left = 0,right = sgt[rtn].size()-1; while(left
>1; if(mid==0) {break;} if(dcmp(x*(sgt[rtn][mid].x-sgt[rtn][mid-1].x)-(sgt[rtn][mid].y-sgt[rtn][mid-1].y))<=0) {left = mid;} else {right = mid-1;} } double ret = -sgt[rtn][left].x*x+sgt[rtn][left].y; return ret; } double query(int rtn,int le,int ri,int lb,int rb,double x) { if(le>=lb && ri<=rb) {return calc(rtn,x);} int mid = (le+ri)>>1; double ret = -INF; if(lb<=mid) {ret = max(ret,query(rtn<<1,le,mid,lb,rb,x));} if(rb>mid) {ret = max(ret,query(rtn<<1|1,mid+1,ri,lb,rb,x));} return ret; }} sgt1,sgt2;void addedge(int u,int v){ en++; e[en].v = v; e[en].nxt = fe[u]; fe[u] = en;}void dfs1(int u){ sz[u] = 1; hvs[u] = 0; for(int i=fe[u]; i; i=e[i].nxt) { int v = e[i].v; if(v==fa[u]) continue; fa[v] = u; dep[v] = dep[u]+1; dfs1(v); sz[u] += sz[v]; if(sz[v]>sz[hvs[u]]) {hvs[u] = v;} }}void dfs2(int u){ cnt++; dfn[u] = cnt; idfn[cnt] = u; if(hvs[u]) {tpn[hvs[u]] = tpn[u]; dfs2(hvs[u]);} for(int i=fe[u]; i; i=e[i].nxt) { int v = e[i].v; if(v==hvs[u] || v==fa[u]) continue; tpn[v] = v; dfs2(v); }}bool query(int u,int v,double x){ double ret1 = -INF,ret2 = -INF; while(tpn[u]!=tpn[v]) { if(dep[fa[tpn[u]]]>dep[fa[tpn[v]]]) { double tmp = sgt1.query(1,1,n,dfn[tpn[u]],dfn[u],x); ret1 = max(ret1,tmp); tmp = sgt2.query(1,1,n,dfn[tpn[u]],dfn[u],x); ret2 = max(ret2,tmp); u = fa[tpn[u]]; } else { double tmp = sgt1.query(1,1,n,dfn[tpn[v]],dfn[v],x); ret1 = max(ret1,tmp); tmp = sgt2.query(1,1,n,dfn[tpn[v]],dfn[v],x); ret2 = max(ret2,tmp); v = fa[tpn[v]]; } } if(dep[u]>dep[v]) swap(u,v); double tmp = sgt1.query(1,1,n,dfn[u],dfn[v],x); ret1 = max(ret1,tmp); tmp = sgt2.query(1,1,n,dfn[u],dfn[v],x); ret2 = max(ret2,tmp); if(dcmp(ret1+ret2)>0) return true; return false;}int main(){ scanf("%d",&n); for(int i=1; i<=n; i++) scanf("%lf",&a1[i]); for(int i=1; i<=n; i++) scanf("%lf",&b1[i]); for(int i=1; i<=n; i++) scanf("%lf",&a2[i]); for(int i=1; i<=n; i++) scanf("%lf",&b2[i]); for(int i=1; i
5e-5) { double mid = (left+right)*0.5; bool ans = query(x,y,mid); if(ans) {left = mid;} else {right = mid;} } printf("%lf\n",left); } return 0;}

转载于:https://www.cnblogs.com/suncongbo/p/11091109.html

你可能感兴趣的文章
ajax2.0
查看>>
C#时间截
查看>>
C语言程序设计II—第九周教学
查看>>
C# 获取系统时间及时间格式转换
查看>>
WCF、WebAPI、WCFREST、WebService之间的区别
查看>>
2018-2019-2-20175332-实验四《Android程序设计》实验报告
查看>>
全栈12期的崛起之捡点儿有用的说说
查看>>
基础类型
查看>>
属性动画
查看>>
标识符
查看>>
Swift 常量&变量
查看>>
Sqli labs系列-less-4 这关好坑!!!
查看>>
路由跟踪工具0trace
查看>>
给大家分享一张CSS选择器优选级图谱 !
查看>>
Win7中不能调试windows service
查看>>
T-SQL触发器,限制一次只能删除一条数据
查看>>
boost库使用:vs2013下boost::container::vector编译出错解决
查看>>
通过httplib2 探索的学习的最佳方式
查看>>
理解运算符重载 4
查看>>
快来熟练使用 Mac 编程
查看>>