zoukankan      html  css  js  c++  java
  • 「ROI 2016 Day1」人烟之山

    「ROI 2016 Day1」人烟之山

    题目大意:

    (n)段折线,(m)个查询点(A)(在折线以上),设折线拐点为(X_i)

    求折线上在查询点投影两边最近的位置(B),且直线(AB)与折线有非边缘的交点 (即从(A)点看过来会被折线遮住)

    题目分析:

    (B,C)点满足的条件就是其旁边的直线(L)(x_A)处的值(>y_A)

    Snipaste_2021-02-20_13-43-59.png

    即找到最近的红色直线

    Solution1 (O(nlog ^2n))

    以求左边为例

    考虑二分答案,求出拐点为(X_i,ige mid)的直线中是否存在红色直线

    也就是求最大值是否(>y_A),维护直线最大值,并且区间查询,可以暴力可持久化李超树来解决

    因为是求左边的,所以每条直线更新的范围(>X_i),李超树区间更新复杂度为(O(log ^2n))

    李超树查询复杂度为(O(log n)),加上二分,查询为(O(log ^2n))

    [ ]

    Solution2 (O(nlog n))

    依然考虑二分,但是这次考虑在线段树上二分

    对于所有的(X_i)建立线段树,区间([L,R])内维护一个(X_i,iin [L,R])的上凸包,静态维护最大值

    凸包可以归并子节点来建立,预处理复杂度为(O(nlog n))

    如果查询直接在凸包上二分,复杂度会增加一个(log n)

    解决方法是:将所有查询的(x_A)排序,然后在凸包上查询时就可以做到线性

    因此复杂度为(O(nlog n))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef double db;
    #define pb push_back
    #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
    char buf[200000],*p1,*p2;
    #define getchar() (((p1==p2)&&(p2=(p1=buf)+fread(buf,1,200000,stdin))),*p1++)
    char IO;
    int rd(){
    	int s=0; int f=0;
    	while(!isdigit(IO=getchar())) f|=IO=='-';
    	do s=(s<<1)+(s<<3)+(IO^'0');
    	while(isdigit(IO=getchar()));
    	return f?-s:s;
    }
    
    const int N=4e5+10,U=1e9+10;
    
    int n,m,K[N],X[N],L[N],R[N];
    ll Y[N];
    struct Que{
    	int x,y,id;
    	bool operator < (const Que __) const {
    		return x<__.x;
    	}
    } Q[N];
    struct Node{
    	int k; ll b;
    	ll operator [] (const ll x) const {
    		return 1ll*k*x+b;
    	}
    	friend db Cross(Node x,Node y){ return 1.0*(y.b-x.b)/(x.k-y.k); }
    	bool operator < (const Node __) const {
    		return k<__.k||(k==__.k && b<__.b);
    	}
    }; 
    vector <Node> H[N<<2];
    vector <Node> ::iterator P[N<<2];
    void Build(int p,int l,int r){
    	if(l==r) return H[p].pb((Node){K[l],Y[l]-1ll*K[l]*X[l]}),P[p]=H[p].begin(),void();
    	int mid=(l+r)>>1;
    	Build(p<<1,l,mid),Build(p<<1|1,mid+1,r);
    	int p1=0,s1=H[p<<1].size(),p2=0,s2=H[p<<1|1].size(),R=-1;
    	auto Ins=[&](Node L) {
    		while(~R && H[p][R].b<=L.b) R--,H[p].pop_back();
    		while(R>0 && Cross(H[p][R],H[p][R-1])>=Cross(H[p][R],L)-1e-8) R--,H[p].pop_back();
    		H[p].pb(L),R++;
    	};
    	while(p1<s1 || p2<s2) {
    		if(p1<s1 && (p2==s2 || H[p<<1][p1]<H[p<<1|1][p2])) Ins(H[p<<1][p1++]);
    		else Ins(H[p<<1|1][p2++]);
    	}
    	P[p]=H[p].begin();
    }
    
    ll Que(int p,int x){
    	while(P[p]+1!=H[p].end() && (*(P[p]+1))[x]>=(*P[p])[x]) P[p]++;
    	return (*P[p])[x];
    }
    int QueL(int p,int l,int r,int x,int qx,int y){
    	if(x<l || Que(p,qx)<=y) return 0;
    	if(l==r) return l;
    	int mid=(l+r)>>1,t;
    	if(x>mid && (t=QueL(p<<1|1,mid+1,r,x,qx,y))) return t;
    	return QueL(p<<1,l,mid,x,qx,y);
    }
    int QueR(int p,int l,int r,int x,int qx,int y){
    	if(x>r || Que(p,qx)<=y) return n+1;
    	if(l==r) return l;
    	int mid=(l+r)>>1,t;
    	if(x<=mid && (t=QueR(p<<1,l,mid,x,qx,y))<=n) return t;
    	return QueR(p<<1|1,mid+1,r,x,qx,y);
    }
    
    int main(){
    	n=rd(),m=rd();
    	rep(i,1,n) {
    		X[i]=X[i-1]+rd(),K[i]=rd();
    		Y[i]=Y[i-1]+1ll*(X[i]-X[i-1])*K[i];
    	}
    	rep(i,1,m) Q[i].x=rd(),Q[i].y=rd(),Q[i].id=i;
    	sort(Q+1,Q+m+1);
    	Build(1,1,n);
    	int p=1;
    	rep(i,1,m) {
    		while(p<=n && X[p]<Q[i].x) p++;
    		L[Q[i].id]=QueL(1,1,n,p-1,Q[i].x,Q[i].y);
    		R[Q[i].id]=QueR(1,1,n,p+(X[p]==Q[i].x),Q[i].x,Q[i].y);
    	}
    	rep(i,1,m) printf("%d %d
    ",X[L[i]],X[R[i]-1]);
    }
    
    
  • 相关阅读:
    关于echarts图表在tab页中width:100%失效的问题
    easyui
    小程序中点击事件传参
    微信小程序实现滚动分页加载更多
    使用jquery如何获取现在时间、并且格式化
    只需两步获取任何微信小程序源码
    怎样修改已经审核通过发布成功的微信小程序
    小程序开发swiper如何实现点击图片自定义跳转
    微信小程序如何提交审核并发布?发布问题:小程序只支持https访问
    小程序填坑之路—读取用户信息、缓存其数据、读取其数据
  • 原文地址:https://www.cnblogs.com/chasedeath/p/14420480.html
Copyright © 2011-2022 走看看