[PKUSC2022]简单计算几何题——扫描线、平衡树

原题链接寄了

题面

n n n 个线段。第 i i i 条线段连接 ( a i , b i ) , ( c i , d i ) (a_i,b_i),(c_i,d_i) (ai,bi),(ci,di)。同时这些直线满足如下性质:

  1. 对于任意线段,满足 c i > a i , d i < b i c_i > a_i,d_i < b_i ci>ai,di<bi
  2. 对于任意两条线段,其不包含任何公共点(包括端点在内)。

Q Q Q 次询问,每次询问给出一个点 ( x i , y i ) (x_i,y_i) (xi,yi)。询问对于所有 n n n 条线段,其落在以 ( − C , − C ) (-C,-C) (C,C) 为左下角, ( x i , y i ) (x_i,y_i) (xi,yi) 为右上角的矩形内的部分的长度之和。

输入格式

第一行一个正整数 n n n

接下来 n n n 行,每行四个整数 a i , b i , c i , d i a_i,b_i,c_i,d_i ai,bi,ci,di

接下来一行一个正整数 Q Q Q

接下来 Q Q Q 行,一行两个整数 x i , y i x_i,y_i xi,yi

输出格式

Q Q Q 行,一行一个非负实数,表示答案。

假定输入的所有的线段长度之和为 S S S, 你的答案被认为是正确的当且仅当对于每次询问,你和标准输入的绝对误差不超过 1 0 − 10 max ⁡ { S , 1 } 10^{-10}\max\{S,1\} 1010max{S,1}

换而言之,假设你的输出是 a a a,标准答案是 b b b,则你的答案被认为是正确的当且仅当 ∣ a − b ∣ ≤ 1 0 − 10 max ⁡ { S , 1 } |a-b|≤10^{-10}\max\{S,1\} ab1010max{S,1}

数据范围

Subtask 1: n , Q ⩽ 50 n,Q\leqslant 50 n,Q50 (8%)

Subtask 2: n , Q ⩽ 5000 n,Q \leqslant 5000 n,Q5000 (10%)

Subtask 3: 线段长度之和不超过 1 0 6 10^6 106 (22%)

Subtask 4: 对于所有的线段,满足 a i + b i = c i + d i a_i+b_i = c_i+d_i ai+bi=ci+di (15%)

Subtask 5: 没特殊限制 (45%)

对于所有测试数据,满足 C = 3 × 1 0 5 , ∣ a i ∣ , ∣ b i ∣ , ∣ c i ∣ , ∣ d i ∣ , ∣ x i ∣ , ∣ y i ∣ ⩽ 3 × 1 0 5 , n ⩽ 1 × 1 0 5 , Q ⩽ 1.5 × 1 0 5 C = 3×10^5, |a_i|,|b_i|,|c_i|,|d_i|,|x_i|,|y_i| \leqslant 3×10^5, n\leqslant 1×10^5,Q\leqslant 1.5×10^5 C=3×105,ai,bi,ci,di,xi,yi3×105,n1×105,Q1.5×105

题解

这题和计算几何没什么关系,就一个SB数据结构题。

首先把答案转化为 x ∈ R , y ≤ y i x\in R,y\le y_i xR,yyi 的长度和减去 x > x i , y ≤ y i x>x_i,y\le y_i x>xi,yyi 的长度和。前者可以直接沿 y y y 轴扫描线,然后分线段与扫描线是否相交两种情况,不相交直接统计和,相交的差分处理。

对于后者,我们仍然分两种情况讨论:
线段与边框不相交,我们可以直接用树状数组维护后缀和;
线段与边框相交,此时由于线段的朝向,最多只会有一个交点,我们分别沿 x x x 轴和 y y y 轴做扫描线,把线段放平衡树上即可求得与两条边框相交的线段长,同样的差分处理。

现在最大的问题是扫描线移动的过程中,平衡树上的线段的次序问题。由于这题的性质,线段没有交点,所以在平衡树上同时存在的线段次序不会变化,直接放心做就好了。

最后是一些精度问题,比如当你要删去平衡树上的某点时,千万不要用double参数的方式来查找它,这样会导致删去一片点或者什么也没删,你只能找到要删的点往父亲爬。

代码

我也不知道是不是对的,反正民间数据过了。

#include<bits/stdc++.h>//JZM yyds!!
#define ll long long
#define lll __int128
#define uns unsigned
#define fi first
#define se second
#define IF (it->fi)
#define IS (it->se)
#define END putchar('\n')
#define lowbit(x) ((x)&-(x))
#define inline jzm
using namespace std;
const int MAXN=6e5+5;
const ll INF=1e18;
ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return f?x:-x;
}
int ptf[50],lpt;
void print(ll x,char c='\n'){
	if(x<0)putchar('-'),x=-x;
	ptf[lpt=1]=x%10;
	while(x>9)x/=10,ptf[++lpt]=x%10;
	while(lpt>0)putchar(ptf[lpt--]^48);
	if(c>0)putchar(c);
}
const double eps=1e-14,E=1;
const int AD=3e5+2,L=1,R=AD<<1;
int n,m;
struct itn{
	int a,b,c,d;double len;
	void readin(){
		a=read(),b=read(),c=read(),d=read();
		len=sqrt(1ll*(a-c)*(a-c)+1ll*(b-d)*(b-d));
	}
	double cty(int y){return c-E*(c-a)/(b-d)*(y-d);}
}f[MAXN];
int X[MAXN>>1],Y[MAXN>>1];
double ans[MAXN>>1];
vector<int>ad[MAXN],sk[MAXN];
#define pii pair<int,int>
mt19937 Rand(114514);
struct fhq{
	int ls,rs,val,fa;double a,b,s,g;fhq(){}
	fhq(double A,double B){
		s=a=A,g=b=B,ls=rs=0,val=Rand();
	}
}t[MAXN];
int root;
void upd(int x){
	t[x].s=t[t[x].ls].s+t[t[x].rs].s+t[x].a;
	t[x].g=t[t[x].ls].g+t[t[x].rs].g+t[x].b;
	if(t[x].ls)t[t[x].ls].fa=x;
	if(t[x].rs)t[t[x].rs].fa=x;
}
pii split(int x,int y,double k){
	t[x].fa=0;
	if(!x)return pii(0,0);
	pii res;
	if(f[x].cty(y)>k)res=split(t[x].ls,y,k),t[x].ls=res.se,upd(x),res.se=x;
	else res=split(t[x].rs,y,k),t[x].rs=res.fi,upd(x),res.fi=x;
	return res;
}
int mergg(int x,int y){
	t[x].fa=t[y].fa=0;
	if(!x||!y)return x^y;
	int res;
	if(t[x].val<t[y].val)t[x].rs=mergg(t[x].rs,y),upd(x),res=x;
	else t[y].ls=mergg(x,t[y].ls),upd(y),res=y;
	return res;
}
void delp(int x){
	int y=mergg(t[x].ls,t[x].rs);
	while(t[x].fa>0){
		if(x==t[t[x].fa].ls)x=t[x].fa,t[x].ls=y,upd(x),y=x;
		else x=t[x].fa,t[x].rs=y,upd(x),y=x;
	}root=y;
}
double bf[MAXN];
void badd(int x,double d){
	for(;x<=R;x+=lowbit(x))bf[x]+=d;
}
double bsum(int x){
	double res=0;
	for(;x>0;x&=(x-1))res+=bf[x];
	return res;
}
void prt(){for(int i=1;i<=m;i++)printf("%.10f\n",ans[i]);}
int main()
{
	n=read();
	for(int i=1;i<=n;i++)f[i].readin();
	m=read();
	for(int i=1;i<=n;i++)ad[f[i].b+AD].push_back(i);
	for(int i=1;i<=m;i++)X[i]=read(),Y[i]=read(),sk[Y[i]+AD].push_back(i);
	for(int i=1;i<=R;i++){
		for(int x:ad[i])badd(f[x].a+AD,f[x].len);
		for(int x:sk[i])ans[x]+=bsum(X[x]+AD-1);
		ad[i].clear();
	}
	for(int i=1;i<=n;i++)ad[f[i].d+AD].push_back(i),ad[f[i].b+AD].push_back(-i);
	for(int i=1;i<=R;i++){
		for(int xx:ad[i]){
			int x=abs(xx);
			if(xx>0){
				pii y=split(root,i-AD,f[x].cty(i-AD));
				double b=f[x].len/(f[x].b-f[x].d);
				t[x]=fhq(b*(R-AD-f[x].d),b);
				root=mergg(y.fi,mergg(x,y.se));
			}else delp(x);
		}
		for(int x:sk[i]){
			ans[x]+=t[root].s-t[root].g*(R-i);
			pii y=split(root,i-AD,X[x]-eps);
			ans[x]-=t[y.se].s-t[y.se].g*(R-i);
			root=mergg(y.fi,y.se);
		}ad[i].clear(),sk[i].clear();
	}
	for(int i=1;i<=n;i++){
		swap(f[i].a,f[i].d),swap(f[i].b,f[i].c);
		ad[f[i].d+AD+1].push_back(i),ad[f[i].b+AD+1].push_back(-i);
	}
	for(int i=1;i<=m;i++)swap(X[i],Y[i]),sk[Y[i]+AD].push_back(i);
	root=0;
	for(int i=1;i<=R;i++){
		for(int xx:ad[i]){
			int x=abs(xx);
			if(xx>0){
				pii y=split(root,i-AD,f[x].cty(i-AD));
				double b=f[x].len/(f[x].b-f[x].d);
				t[x]=fhq(b*(f[x].b+AD),b);
				root=mergg(y.fi,mergg(x,y.se));
			}else delp(x);
		}
		for(int x:sk[i]){
			pii y=split(root,i-AD,X[x]-eps);
			ans[x]-=t[y.fi].s-t[y.fi].g*i;
			root=mergg(y.fi,y.se);
		}ad[i].clear(),sk[i].clear();
	}prt();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值