• YbtOJ#853平面标记【整体二分,凸壳】


    正题

    题目链接:http://www.ybtoj.com.cn/contest/119/problem/3


    题目大意

    给出\(n\)个点\((x_i,y_i)\)\(m\)次给出\((k_i,a_i)\)表示标记所有满足

    \[y_j>\frac{k_i}{x_j^{a_i}} \]

    的未标记点

    求每个点的标记时间

    \(1\leq n,m\leq 10^5,1<a_i<10\)


    解题思路

    全是乘法所以可以先左右取\(ln\)就是

    \[ln(y_j)>ln(k_i)-ln(x_j)\times a_i \]

    \(x,y,k\)\(ln\)然后就是一个顺眼的式子

    \[x_j\times a_i+y_i>k_i \]

    虽然原题说\((x_i,y_i)\)是点,但是我们可以换个思路,把\((x_i,y_i)\)看成边(\(f(z)=x_iz+y_i\)),\((a_i,k_i)\)看成是点,然后问在每条边下面的编号最小的点是哪个。

    这个就很好解决了,考虑整体二分。每次要考虑对于一条边是否有在\([L,mid]\)编号的点在它下面。可以对于所有的\([L,mid]\)的点拿出来构成一个下凸壳,然后根据每条边的斜率二分出一个最下面的点,然后只拿这个点判断就好了。

    这样就是\(O(n\log^2 n)\)的了,如果肯写归并排序和凸壳用单调队列维护是可以做到\(O(n\log n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int N=1e5+10;
    int n,m,f[N],s[N],p[N],p1[N],p2[N],top,pos[N];
    double x[N],y[N],k[N],z[N];
    double xj(double x1,double y1,double x2,double y2)
    {return x1*y2-x2*y1;}
    double xl(int a,int b,int c){
    	double y1=k[b]-k[a],x1=z[b]-a[z];
    	double y2=k[c]-k[a],x2=z[c]-z[a];
    	return xj(x1,y1,x2,y2);
    }
    bool cmp(int x,int y)
    {return z[x]<z[y];}
    void solve(int Ln,int Rn,int Lm,int Rm){
    	if(Ln>Rn)return;
    	if(Lm==Rm){
    		for(int i=Ln;i<=Rn;i++)
    			f[p[i]]=Lm;
    		return;
    	}
    	int mid=(Lm+Rm)>>1;top=0;
    	sort(pos+Lm,pos+1+mid,cmp);
    	for(int i=Lm;i<=mid;i++){
    		while(top>1&&xl(s[top-1],s[top],pos[i])<=0)top--;
    		s[++top]=pos[i];
    	}
    	sort(pos+Lm,pos+1+mid);
    	int cnt1=0,cnt2=0;
    	for(int i=Ln;i<=Rn;i++){
    		int l=1,r=top-1;
    		while(l<=r){
    			int m=(l+r)>>1;
    			if(xj(z[s[m+1]]-z[s[m]],k[s[m+1]]-k[s[m]],1,x[p[i]])>0)l=m+1;
    			else r=m-1;
    		}
    		if(x[p[i]]*z[s[l]]+y[p[i]]>k[s[l]])p1[++cnt1]=p[i];
    		else p2[++cnt2]=p[i];
    	}
    	for(int i=1;i<=cnt1;i++)p[i+Ln-1]=p1[i];
    	for(int i=1;i<=cnt2;i++)p[Ln+cnt1+i-1]=p2[i];
    	solve(Ln,Ln+cnt1-1,Lm,mid);
    	solve(Ln+cnt1,Rn,mid+1,Rm);
    	return;
    }
    int main()
    {
    	freopen("analysis.in","r",stdin);
    //	freopen("analysis.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
    		scanf("%lf%lf",&x[i],&y[i]);
    		x[i]=log(x[i]);y[i]=log(y[i]);
    		p[i]=i;
    	}
    	for(int i=1;i<=m;i++){
    		scanf("%lf%lf",&k[i],&z[i]);
    		k[i]=log(k[i]);pos[i]=i;
    	}
    	solve(1,n,1,m);
    	for(int i=1;i<=n;i++)
    		if(f[i]==m)puts("-1");
    		else printf("%d\n",f[i]);
    	return 0;
    }
    
  • 相关阅读:
    动态规划(最长公共序列,最长上升序列)
    滴滴笔试--算术转移(20190827)
    线段树和树状数组
    pair和list学习
    数据结构--树(建立、遍历)
    tmux常用命令与快捷键
    机器学习实战-逻辑回归
    字符流中第一个重复的字符
    机器学习实战-朴素贝叶斯
    Python第三方库
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14421283.html
Copyright © 2020-2023  润新知