原文链接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; }