zoukankan      html  css  js  c++  java
  • 6217. 【NOI2019模拟2019.6.14】最大面积

    有一堆向量(A_i),多次询问向量(P),查(max_{Lle R}P imes sum_{i=L}^R A_i)

    (nle 10^5)


    设向量(P=(A,B)),区间和为((X,Y))。需要最大化(F=AY-BX)。于是(Y=frac{B}{A}X+frac{F}{A})。以(A>0)为例,相当于在((X,Y))引一条斜率(frac{B}{A})的直线,求所有这样的((X,Y))引出的直线的最大截距。

    于是答案一定在所有((X,Y))组成的凸包上。如果能求出这个凸包,就可以直接在上凸壳上二分在(O(lg))的时间内得到答案。

    以下是构建这个凸包的方式:分治构造,求出([L,mid],[mid+1,R])内的凸包,以及跨过中点的。对于跨过中点的,对左区间做后缀和,对右区间做前缀和,两者闵科夫斯基和。然后把得到的这三个凸包合并起来。

    因为一个分治区间最多贡献区间长度的点数,总点数不超过区间总长,即(O(nlg n))。因为计算跨过中点时还需要求凸包,时间(O(nlg^2n))

    (诶所以我的实现方式是(O(nlg^3 n))?)

    upd:
    其实不需要每次合并左右区间及跨过中点的凸包,直接在最后把所有凸包合并即可。这样做时间是(O(nlg^2 n))
    计算跨过中点贡献的时候,应该可以维护好横坐标的顺序,然后归并,这部分时间(O(nlg n))。但是合并凸包时间还是这么多(如果在分治的时候合并,虽然可以做到线性,但是由于一层点数是(O(lenlg len)),总共还是(O(nlg^2 n))的时间。)


    第一次写凸包(平常只是维护个凸壳)+第一次写闵科夫斯基和。

    其实凸包可以直接分别维护上凸壳和下凸壳,应该更好写,也方便优化(在已经排好序的情况下,凸包合并应该可以线性做)

    这次用极角排序的方式写凸包,有点细节。

    求凸包和闵科夫斯基和的时候都需要找到最左端(坐标相同就最下端)的点作为基准点,从它开始跑,因为这样保证了这个点一定在凸包上。

    然后凸包合并的时候我重新求了一次凸包,所以时间多了一个(O(lg))

    using namespace std;
    #include <bits/stdc++.h>
    #define N 100005
    #define ll long long
    #define db long double
    int n,Q;
    struct DOT{
    	ll x,y;
    	DOT(ll _x=0,ll _y=0){x=_x,y=_y;}
    	db len2(){return (db)x*x+(db)y*y;}
    } d[N];
    bool operator<(DOT a,DOT b){return a.x<b.x || a.x==b.x && a.y<b.y;}
    DOT operator+(DOT a,DOT b){return (DOT){a.x+b.x,a.y+b.y};}
    DOT operator-(DOT a,DOT b){return (DOT){a.x-b.x,a.y-b.y};}
    db cro(DOT a,DOT b){return (db)a.x*b.y-(db)a.y*b.x;}
    vector<DOT> q[N*4],u,v;
    vector<DOT> qa;
    bool cmparc(DOT a,DOT b){return cro(a,b)>0 || cro(a,b)==0 && a.len2()<b.len2();}
    void getcon(vector<DOT> &w){
    	DOT O=w[0];
    	for (int i=1;i<w.size();++i)
    		O=min(O,w[i]);
    	qa.clear();
    	for (int i=0;i<w.size();++i)
    		if (w[i].x!=O.x || w[i].y!=O.y)
    			qa.push_back(w[i]-O);
    	sort(qa.begin(),qa.end(),cmparc);
    	static DOT st[N*20];
    	int tp;
    	st[tp=1]=DOT(0,0);
    	for (int i=0;i<qa.size();++i){
    		while (tp>1 && cro(st[tp]-st[tp-1],qa[i]-st[tp-1])<=0)
    			--tp;
    		st[++tp]=qa[i];
    	}
    	while (tp>1 && cro(st[tp]-st[tp-1],DOT(0,0)-st[tp-1])<0)
    		--tp;
    	w.clear();
    	for (int i=1;i<=tp;++i)
    		w.push_back(st[i]+O);
    }
    void getsum(vector<DOT> &w){
    	DOT O=u[0]+v[0];
    	u.push_back(u[0]);
    	v.push_back(v[0]);
    	w.clear();
    	w.push_back(O);
    	int i=0,j=0;
    	while (i<u.size()-1 || j<v.size()-1)
    		if (i<u.size()-1 && (j==v.size()-1 || cro(u[i+1]-u[i],v[j+1]-v[j])>0))
    			w.push_back(O=O+u[i+1]-u[i]),i++;
    		else
    			w.push_back(O=O+v[j+1]-v[j]),j++;
    	w.pop_back();
    }
    void merge(vector<DOT> &a,vector<DOT> &b){
    	for (int i=0;i<b.size();++i)
    		a.push_back(b[i]);
    	b.clear();
    }
    void divide(int k,int l,int r){
    	if (l==r){
    		q[k].push_back(d[l]);
    		return;
    	}
    	int mid=l+r>>1;
    	divide(k<<1,l,mid);
    	divide(k<<1|1,mid+1,r);
    	u.clear(),v.clear();
    	DOT s(0,0);
    	for (int i=mid;i>=l;--i)
    		s=s+d[i],u.push_back(s);
    	s=DOT(0,0);
    	for (int i=mid+1;i<=r;++i)
    		s=s+d[i],v.push_back(s);
    	getcon(u);
    	getcon(v);
    	getsum(q[k]);
    	merge(q[k],q[k<<1]);
    	merge(q[k],q[k<<1|1]);
    	getcon(q[k]);
    }
    vector<DOT> f,g;
    ll ask(vector<DOT> &h,DOT p){
    	ll ans=0;
    	for (int i=0;i<h.size();++i){
    		ans=max(ans,(ll)cro(p,h[i]));
    	}
    	return ans;
    	int l=0,r=(int)h.size()-2,res=h.size()-1;
    	while (l<=r){
    		int mid=l+r>>1;
    		if (cro(p,h[mid+1]-h[mid])<=0)
    			r=(res=mid)-1;
    		else
    			l=mid+1;
    	}
    	return cro(p,h[res]);
    }
    int main(){
    	freopen("in.txt","r",stdin);
    	freopen("out.txt","w",stdout);
    //	freopen("area.in","r",stdin);
    //	freopen("area.out","w",stdout);
    	scanf("%d%d",&n,&Q);
    	for (int i=1;i<=n;++i)
    		scanf("%lld%lld",&d[i].x,&d[i].y);
    	divide(1,1,n);
    	DOT lef=q[1][0],rig=q[1][0];
    	int pos=0;
    	for (int i=1;i<q[1].size();++i)
    		if (rig<q[1][i])
    			rig=q[1][i],pos=i;
    	for (int i=0;i<=pos;++i) f.push_back(DOT(q[1][i].x,-q[1][i].y));
    	g.push_back(lef);
    	for (int i=q[1].size()-1;i>=pos;--i) g.push_back(q[1][i]);
    	while (Q--){
    		ll A,B;
    		scanf("%lld%lld",&A,&B);
    		ll ans=0;
    		if (A==0){
    			if (B==0)
    				ans=0;
    			else
    				ans=max(-B*lef.x,-B*rig.x);
    		}
    		else if (A>0)
    			ans=ask(g,DOT(A,B));
    		else
    			ans=ask(f,DOT(-A,B));
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    原子操作--sync/atomic的用法
    基础的排序算法以及查找算法
    (三)MySQL终极篇
    (二)MySQL中级篇
    数据库表添加索引对性能的影响
    事务的四大特性以及事务的隔离级别
    int 和Integer
    数据库三范式
    Java反射
    获取Class实例的三种方式
  • 原文地址:https://www.cnblogs.com/jz-597/p/14737604.html
Copyright © 2011-2022 走看看