• 2018牛客网暑假ACM多校训练赛(第四场)E Skyline 线段树 扫描线


    原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round4-E.html

    题目传送门 - https://www.nowcoder.com/acm/contest/142/E

    题意

      给定二维平面上的 $n$ 个点,第 $i$ 个点的坐标是 $(x_i,y_i)$ ,第 $i$ 个点出现的概率是 $a_i imes b_i^{-1}$ 。

      现在让你求  [满足 “存在 $i$ 使得 $xleq x_i,yleq y_i$ ”的点 $(x,y)$  的数量]  的期望。答案对于 $10^9+7$ 取模。

      多组数据,$nleq 10^5,sum nleq 10^6, m{Time Limit = 10s}$

    题解

      出题人大概是着急有事吧。他讲的太快了这题不知道说了什么。只记得我去存了一下队友比赛时候的 AC 代码然后回来发现他讲了好几题。

      下面是我们队的做法。

      首先,我们考虑补集转化: 期望点数 = 可能的点 - 不满足条件的点数的期望

      其中可能的点我们设定为 左下角为 (1,1) ,右上角为 $ (max_x,max_y)$ ,其中右上角的两个坐标值分别为输入点的两个坐标值的最大值。

      那么我们考虑一个点对于“不满足条件的点数的期望”的贡献。这个贡献显然是 $1 imes $ “在这个点左上方向的所有点都不出现的概率” 。

      我们只需要用一个算法把所有的点的贡献加起来就可以了。

      首先,我们离散化一下。然后线段树扫描线,从上往下一行一行操作。一个点的"操作"就是对 $x$ 坐标在其左侧的区间乘上 它不出现的概率。这个显然可以线段树区间乘法。每操作完一行,统计一下这一行与下一行之间区域的答案,并更新总答案。

      时间复杂度 $O (nlog n)$ 。

      不进行补集转化应该也可以做吧,但是会难写一些吧。比如我一开始用树状数组写到弃疗……

    代码

    %:pragma GCC optimize("Ofast")
    %:pragma GCC optimize("inline")
    #include <bits/stdc++.h>
    using namespace std;
    const int N=100005,mod=1e9+7;
    namespace HashTable{
    	int hs,Ha[N];
    	void clear(){hs=0;memset(Ha,0,sizeof Ha);}
    	void push(int x){Ha[++hs]=x;}
    	void HASH(){
    		sort(Ha+1,Ha+hs+1);
    		int _hs=1;
    		for (int i=2;i<=hs;i++)
    			if (Ha[i]!=Ha[i-1])
    				Ha[++_hs]=Ha[i];
    		hs=_hs;
    	}
    	int find(int x){return lower_bound(Ha+1,Ha+hs+1,x)-Ha;}
    }
    using namespace HashTable;
    inline int read(){
    	int x=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    	return x;
    }
    inline int Pow(int x,int y){
    	int ans=1;
    	for (;y;y>>=1,x=1LL*x*x%mod)
    		if (y&1)
    			ans=1LL*ans*x%mod;
    	return ans;
    }
    struct Point{
    	int x,y,p;
    }a[N];
    bool cmp(Point a,Point b){
    	if (a.y!=b.y)
    		return a.y>b.y;
    	return a.x>b.x;
    }
    struct Seg{
    	int v,add;
    }t[N<<2];
    inline void build(int rt,int L,int R){
    	t[rt].add=1;
    	if (L==R){
    		t[rt].v=Ha[L]-Ha[L-1];
    		return;
    	}
    	int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
    	build(ls,L,mid);
    	build(rs,mid+1,R);
    	t[rt].v=(t[ls].v+t[rs].v)%mod;
    }
    inline void pushdown(int rt){
    	int &v=t[rt].add;
    	if (v==1)
    		return;
    	int ls=rt<<1,rs=ls|1;
    	t[ls].v=1LL*t[ls].v*v%mod,t[ls].add=1LL*t[ls].add*v%mod;
    	t[rs].v=1LL*t[rs].v*v%mod,t[rs].add=1LL*t[rs].add*v%mod;
    	v=1;
    }
    inline void update(int rt,int L,int R,int x,int d){
    	if (R<=x){
    		t[rt].v=1LL*t[rt].v*d%mod;
    		t[rt].add=1LL*t[rt].add*d%mod;
    		return;
    	}
    	pushdown(rt);
    	int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
    	update(ls,L,mid,x,d);
    	if (x>mid)
    		update(rs,mid+1,R,x,d);
    	t[rt].v=(t[ls].v+t[rs].v)%mod;
    }
    inline void solve(){
    	int n=read();
    	clear();
    	for (int i=1;i<=n;i++){
    		a[i].x=read(),a[i].y=read();
    		push(a[i].x);
    		int A=read(),B=read();
    		a[i].p=1LL*A*Pow(B,mod-2)%mod;
    	}
    	HASH();
    	for (int i=1;i<=n;i++)
    		a[i].x=find(a[i].x);
    	sort(a+1,a+n+1,cmp);
    	build(1,1,hs);
    	int ans=0;
    	for (int i=1,j;i<=n;i=j+1){
    		for (j=i;j<n&&a[j+1].y==a[i].y;j++);
    		for (int k=i;k<=j;k++)
    			update(1,1,hs,a[k].x,(mod+1-a[k].p)%mod);
    		ans=(1LL*(a[i].y-a[j+1].y)*(Ha[hs]-t[1].v+mod)+ans)%mod;
    	}
    	printf("%d
    ",ans);
    }
    int main(){
    	for (int T=read();T;T--)
    		solve();
    	return 0;
    }
    

      

  • 相关阅读:
    java oop第09章_JDBC02(CRUD操作)
    java 与日期转换相关的方法(java.util.date类型和java.sql.date类型互相转换)、随机字符串生成方法、UUID生产随机字符串
    Java oop第08章_JDBC01(入门)
    java 数组中的数值反转输出
    java oop第07章_集合框架
    Java oop创建自定义异常
    java oop遍历List和Map的几种方法
    java oop第06章_异常处理
    Java oop第05章_多态、接口
    信开发 新浪SAE开发平台 验证Token 一直失败
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round4-E.html
Copyright © 2020-2023  润新知