P6106 [Ynoi2010] Self Adjusting Top Tree gxy001

首先,我们只考虑斜率为正的线段,对于斜率为负的,我们把平面倒过来再做一次就行了。

我们发现这题的贡献可减,所以考虑将一个询问拆成四个前缀矩形的询问。

对答案有贡献的线段分为两种,与矩形边界无交和有交的。

无交的线段的右上角一定被包含了,把贡献放在右上角的点上,做一个二维偏序就可以解决;

有交的线段一定只和上边界和右边界之一有交(交在顶点上的扰动一下就变成交在边上了),我们分两遍处理,只考虑与右边界相交的线段。考虑扫描线(一条竖线),维护所有线段与扫描线的交点,由于线段两两不交,交点顺序不会改变,所以考虑用平衡树维护线段到当前扫描线的长度和。查询的时候就是查一个前缀的长度和。

#include<cstdio>
#include<chrono>
#include<random>
#include<cstdlib>
#include<algorithm>
#define BUFSIZE 10000000
struct read{
	char buf[BUFSIZE],*p1,*p2,c,f;
	read():p1(buf),p2(buf){}
	char gc(void){
		return p1==p2&&(p2=buf+fread(p1=buf,1,BUFSIZE,stdin),p1==p2)?EOF:*p1++;
	}
	read& operator >>(int& x){
		for(c=gc(),f=0,x=0;c!=EOF&&(c<'0'||c>'9');c=gc())if(c=='-')f=1;
		if(f)for(;c>='0'&&c<='9';c=gc())x=x*10-(c-'0');
		else for(;c>='0'&&c<='9';c=gc())x=x*10+(c-'0');
		return *this;
	}
}in;
inline int rand(int l,int r){
    return rand()%(r-l+1)+l;
}
int n,m;
struct node{
	int x1,x2,y1,y2;
	double k,len;
}a[100010];
struct rect{
	int x1,x2,y1,y2;
}q[100010];
double S;
double ans[100010];
namespace sub1{
	struct node{
		double v;
		int op,x,y;
		node(int o,int a,int b,double val):v(val),op(o),x(a),y(b){}
		node():v(),op(),x(),y(){}
	}a[500010];
	int cnt;
	double c[1000010];
	void update(int i,double v){
		for(;i<=1000000;i+=i&-i) c[i]+=v;
	}
	double query(int i){
		double ans=0;
		for(;i;i&=i-1) ans+=c[i];
		return ans;
	}
	void clear(){
		cnt=0;
		for(int i=1;i<=1000000;i++) c[i]=0;
	}
	void work(){
		std::sort(a+1,a+cnt+1,[](node const &x,node const &y){return x.x==y.x?x.op<y.op:x.x<y.x;});
		for(int i=1;i<=cnt;i++)
			if(a[i].op==0) update(a[i].y,a[i].v);
			else ans[a[i].op]+=a[i].v*query(a[i].y);	
	}
}
namespace sub2{
	double const eps=5e-7;
	int cnt,ct,rt;
	struct node{
		double v1,v2,y;
		int op,x;
		node(int o,int a,double b,double w1=0,double w2=0):v1(w1),v2(w2),y(b),op(o),x(a){}
		node():v1(),v2(),y(),op(),x(){}
	}a[600010];
	struct nd{
		double v1,v2,v3,v4,sum,s;
		int ls,rs,tag,sz;
	}tr[100010];
	void addtag(int x,int v){
		if(x){
			tr[x].tag+=v;
			tr[x].v1+=tr[x].v2*v;
			tr[x].v3+=tr[x].v4*v;
			tr[x].sum+=tr[x].s*v;
		}
	}
	void pushdown(int x){
		if(tr[x].tag){
			addtag(tr[x].ls,tr[x].tag);
			addtag(tr[x].rs,tr[x].tag);
			tr[x].tag=0;
		}
	}
	void pushup(int x){
		tr[x].sum=tr[x].v3+tr[tr[x].ls].sum+tr[tr[x].rs].sum;
		tr[x].s=tr[x].v4+tr[tr[x].ls].s+tr[tr[x].rs].s;
		tr[x].sz=1+tr[tr[x].ls].sz+tr[tr[x].rs].sz;
	}
	int merge(int x,int y){
		if(!x||!y) return x|y;
		if(rand(1,tr[x].sz+tr[y].sz)<=tr[x].sz){
			pushdown(x);
			tr[x].rs=merge(tr[x].rs,y);
			pushup(x);
			return x;
		}else{
			pushdown(y);
			tr[y].ls=merge(x,tr[y].ls);
			pushup(y);
			return y;
		}
	}
	void split(int p,double v,int &x,int &y){
		if(!p) return x=y=0,void();
		pushdown(p);
		if(tr[p].v1<=v+eps) x=p,split(tr[x].rs,v,tr[x].rs,y);
		else y=p,split(tr[y].ls,v,x,tr[y].ls);
		pushup(p);
	}
	void splitsz(int p,int v,int &x,int &y){
		if(!p) return x=y=0,void();
		pushdown(p);
		if(tr[tr[p].ls].sz<v) x=p,splitsz(tr[x].rs,v-tr[tr[p].ls].sz-1,tr[x].rs,y);
		else y=p,splitsz(tr[y].ls,v,x,tr[y].ls);
		pushup(p);
	}
	void del(double x){
		int a,b,c;
		split(rt,x,a,b);
		splitsz(a,tr[a].sz-1,a,c);
		rt=merge(a,b);
	}
	void ins(double x,double a,double b){
		++ct;
		tr[ct].v1=x,tr[ct].v2=b,tr[ct].v3=0,tr[ct].v4=a;
		tr[ct].sum=0,tr[ct].s=tr[ct].v4;
		tr[ct].ls=tr[ct].rs=tr[ct].tag=0,tr[ct].sz=1;
		int p,d;
		split(rt,x,p,d);
		rt=merge(p,merge(ct,d));
	}
	double query(double x){
		int a,b;
		split(rt,x,a,b);
		double ans=tr[a].sum;
		rt=merge(a,b);
		return ans;
	}
	void clear(){
		cnt=ct=0;
	}
	int p;
	void work(){
		p=rt=0;
		std::sort(a+1,a+cnt+1,[](node const &x,node const &y){return x.x==y.x?x.op<y.op:x.x<y.x;});
		for(int i=1;i<=cnt;i++){
			if(p!=a[i].x) addtag(rt,a[i].x-p),p=a[i].x;
			if(a[i].op==-1) del(a[i].y);
			else if(a[i].op==0) ins(a[i].y,a[i].v1,a[i].v2);
			else ans[a[i].op]+=a[i].v1*query(a[i].y);
		}
	}
}
int main(){
	in>>n;
	for(int i=1;i<=n;i++){
		in>>a[i].x1>>a[i].y1>>a[i].x2>>a[i].y2;
		if(a[i].x2<a[i].x1) std::swap(a[i].x1,a[i].x2),std::swap(a[i].y1,a[i].y2);
		a[i].k=1.0*(a[i].y2-a[i].y1)/(a[i].x2-a[i].x1);
		S+=(a[i].len=sqrt(1.0*(a[i].x2-a[i].x1)*(a[i].x2-a[i].x1)+1.0*(a[i].y2-a[i].y1)*(a[i].y2-a[i].y1)));
	}	
	in>>m;
	for(int i=1;i<=m;i++) in>>q[i].x1>>q[i].y1>>q[i].x2>>q[i].y2;
	for(int i=1;i<=n;i++) if(a[i].k>0) sub1::a[++sub1::cnt]=sub1::node(0,a[i].x2,a[i].y2,a[i].len);
	for(int i=1;i<=m;i++) sub1::a[++sub1::cnt]=sub1::node(i,q[i].x2,q[i].y2,1),sub1::a[++sub1::cnt]=sub1::node(i,q[i].x1,q[i].y2,-1),sub1::a[++sub1::cnt]=sub1::node(i,q[i].x2,q[i].y1,-1),sub1::a[++sub1::cnt]=sub1::node(i,q[i].x1,q[i].y1,1);
	sub1::work();
	for(int i=1;i<=n;i++) if(a[i].k>0) sub2::a[++sub2::cnt]=sub2::node(-1,a[i].x2,a[i].y2),sub2::a[++sub2::cnt]=sub2::node(0,a[i].x1,a[i].y1,a[i].len/(a[i].x2-a[i].x1),a[i].k);
	for(int i=1;i<=m;i++) sub2::a[++sub2::cnt]=sub2::node(i,q[i].x2,q[i].y2-1e-6,1),sub2::a[++sub2::cnt]=sub2::node(i,q[i].x2,q[i].y1-1e-6,-1),sub2::a[++sub2::cnt]=sub2::node(i,q[i].x1,q[i].y2-1e-6,-1),sub2::a[++sub2::cnt]=sub2::node(i,q[i].x1,q[i].y1-1e-6,1);
	sub2::work();
	sub2::clear();
	for(int i=1;i<=n;i++) if(a[i].k>0) sub2::a[++sub2::cnt]=sub2::node(-1,a[i].y2,a[i].x2),sub2::a[++sub2::cnt]=sub2::node(0,a[i].y1,a[i].x1,a[i].len/(a[i].y2-a[i].y1),1/a[i].k);
	for(int i=1;i<=m;i++) sub2::a[++sub2::cnt]=sub2::node(i,q[i].y2,q[i].x2,1),sub2::a[++sub2::cnt]=sub2::node(i,q[i].y2,q[i].x1,-1),sub2::a[++sub2::cnt]=sub2::node(i,q[i].y1,q[i].x2,-1),sub2::a[++sub2::cnt]=sub2::node(i,q[i].y1,q[i].x1,1);
	sub2::work();
	for(int i=1;i<=m;i++){
		q[i].y1=1000001-q[i].y1;
		q[i].y2=1000001-q[i].y2;
		std::swap(q[i].y1,q[i].y2);
	}
	for(int i=1;i<=n;i++) a[i].y1=1000001-a[i].y1,a[i].y2=1000001-a[i].y2;
	sub1::clear();
	for(int i=1;i<=n;i++) if(a[i].k<0) sub1::a[++sub1::cnt]=sub1::node(0,a[i].x2,a[i].y2,a[i].len);
	for(int i=1;i<=m;i++) sub1::a[++sub1::cnt]=sub1::node(i,q[i].x2,q[i].y2,1),sub1::a[++sub1::cnt]=sub1::node(i,q[i].x1,q[i].y2,-1),sub1::a[++sub1::cnt]=sub1::node(i,q[i].x2,q[i].y1,-1),sub1::a[++sub1::cnt]=sub1::node(i,q[i].x1,q[i].y1,1);
	sub1::work();
	sub2::clear();
	for(int i=1;i<=n;i++) if(a[i].k<0) sub2::a[++sub2::cnt]=sub2::node(-1,a[i].x2,a[i].y2),sub2::a[++sub2::cnt]=sub2::node(0,a[i].x1,a[i].y1,a[i].len/(a[i].x2-a[i].x1),-a[i].k);
	for(int i=1;i<=m;i++) sub2::a[++sub2::cnt]=sub2::node(i,q[i].x2,q[i].y2-1e-6,1),sub2::a[++sub2::cnt]=sub2::node(i,q[i].x2,q[i].y1-1e-6,-1),sub2::a[++sub2::cnt]=sub2::node(i,q[i].x1,q[i].y2-1e-6,-1),sub2::a[++sub2::cnt]=sub2::node(i,q[i].x1,q[i].y1-1e-6,1);
	sub2::work();
	sub2::clear();
	for(int i=1;i<=n;i++) if(a[i].k<0) sub2::a[++sub2::cnt]=sub2::node(-1,a[i].y2,a[i].x2),sub2::a[++sub2::cnt]=sub2::node(0,a[i].y1,a[i].x1,a[i].len/(a[i].y2-a[i].y1),-1/a[i].k);
	for(int i=1;i<=m;i++) sub2::a[++sub2::cnt]=sub2::node(i,q[i].y2,q[i].x2,1),sub2::a[++sub2::cnt]=sub2::node(i,q[i].y2,q[i].x1,-1),sub2::a[++sub2::cnt]=sub2::node(i,q[i].y1,q[i].x2,-1),sub2::a[++sub2::cnt]=sub2::node(i,q[i].y1,q[i].x1,1);
	sub2::work();
	for(int i=1;i<=m;i++) printf("%.6f\n",ans[i]/S+1e-12);
	return 0;
}