1. 程式人生 > 其它 >P6106 [Ynoi2010] Self Adjusting Top Tree

P6106 [Ynoi2010] Self Adjusting Top Tree

P6106 [Ynoi2010] Self Adjusting Top Tree

題意

給出平面直角座標系上若干不與座標軸平行的處於第一象限的互不相交的線段,多次詢問平面中一個第一象限的矩形與這些線段相交部分的長度長度和與所有線段長度和的比值。給出的所有座標 \(\in[1,10^6]\)

思路

假設所有線段的斜率都是正的,考慮將詢問差分成四個字首矩形。我們只需要考慮統計若干斜率為正的互不相交的線段與一個字首矩形的交就行了。

經典套路:若線段互不相交,在掃描線時其相對順序不會變。比如在用與 \(y\) 軸平行的直線做掃描線時線段與其相交的 \(y\) 座標相對大小不會變。再比如,用以原點為端點的射線做掃描線時線段與其交點到原點距離的相對大小不會變

因此,我們用平行於 \(y\) 軸的直線做掃描線,用平衡樹維護區間線段長度的和。即,維護單位 \(x\) 區間線段長度增量,維護區間線段長度和,支援區間加,支援插入刪除。因為線段斜率為正且詢問為字首矩形,所以沒有線段另一端不在矩形內的情況。這樣就能統計且恰好統計所有與矩形右側相交的線段的長度和。

統計所有與矩形上側相交的長度和只需要將掃描線變為與 \(x\) 平行的再做一遍就可以了。為了使與頂點交的線段只統計一次,可以將翻轉前後其中一次的所有查詢減去 eps 使其不合法。

對於所有完全被包含的線段,發現只要線段右上端在矩形內就全部在矩形內。做一遍二維數點即可。

對於所有斜率為負的線段,將它們和詢問矩形上下反轉,然後再做一遍上述過程即可。

至此,所有的貢獻被統計完畢。實現時請注意細節。

程式碼我覺得我寫的還行,就放出來,看懂了的應該挺容易實現的,沒看懂的可以參考程式碼。

實現

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int w=0,x=0;char c=getchar();
	while(!isdigit(c))w|=c=='-',c=getchar();
	while(isdigit(c))x=x*10+(c^48),c=getchar();
	return w?-x:x;
}
namespace star
{
	const int maxn=4e5+10,maxm=1e6+10,N=1e6+1;
	double eps=1e-6;
	int n,m,cnt[2],c[2][maxn];
	double ans[maxn];
	struct vec{
		int x,y;
		vec(int x=0,int y=0):x(x),y(y){}
		inline void updown(){y=N-y;}
	};
	int X;
	struct seg{
		vec a,b;
		seg(){}
		seg(vec a,vec b):a(a),b(b){}
		inline double length(){return sqrt(1.*(b.y-a.y)*(b.y-a.y)+1.*(b.x-a.x)*(b.x-a.x));}
		inline void updown(){a.updown(),b.updown();}
		inline double y() {return a.y+1.0*(X-a.x)*(b.y-a.y)/(b.x-a.x);}
	}a[maxn],b[2][maxn];
	struct que{
		int x;
		double y;
		int tp,id;
		que(){}
		que(int x,double y,int tp,int id):x(x),y(y),tp(tp),id(id){}
		bool operator < (const que &b) const {return x<b.x;}
	}q[maxn<<2];
	#define ls son[x][0]
	#define rs son[x][1]
	double s1[maxn],s2[maxn],s[maxn],tag[maxn],sum[maxn];
	int tot,rt,e[maxn],son[maxn][2],rnd[maxn];
	inline int newnode(int i){e[++tot]=i,s1[tot]=s[tot]=a[i].length()/(a[i].b.x-a[i].a.x),s2[tot]=tag[tot]=son[tot][0]=son[tot][1]=sum[tot]=0,rnd[tot]=rand();return tot;}
	inline void add(int x,int a){s2[x]+=a*s1[x],sum[x]+=a,tag[x]+=a;}
	inline void pushdown(int x){if(tag[x]) add(ls,tag[x]),add(rs,tag[x]),tag[x]=0;}
	inline void pushup(int x){s2[x]=s2[ls]+s2[rs]+sum[x]*s[x],s1[x]=s1[ls]+s1[rs]+s[x];}
	void split(int x,double k,int &a,int &b){
		if(!x) return a=b=0,void();
		pushdown(x);
		if(k>=star::a[e[x]].y()) a=x,split(rs,k,rs,b);
			else b=x,split(ls,k,a,ls);
		pushup(x);
	}
	int merge(int a,int b){
		if(!a or !b) return a|b;
		if(rnd[a]<rnd[b]){
			pushdown(a),son[a][1]=merge(son[a][1],b),pushup(a);
			return a;
		}else{
			pushdown(b),son[b][0]=merge(a,son[b][0]),pushup(b);
			return b;
		}
	}
	inline void insert(int i){
		int x,y;
		split(rt,a[i].y(),x,y);
		rt=merge(merge(x,newnode(i)),y);
	}
	inline void update(int i){
		int a,b;
		split(rt,star::a[i].y(),a,b);
		static int st[maxn];
		int top=0,x,y;
		for(y=0,x=a;rs;y=x,x=rs) pushdown(x),st[++top]=x;
		if(e[x]!=i) return rt=merge(merge(a,newnode(i)),b),void();
		if(!y) a=son[a][0];
		else son[y][1]=merge(ls,rs);
		while(top) pushup(st[top--]);
		rt=merge(a,b);
	}
	inline double query(double k){
		int x,y;
		split(rt,k,x,y);
		double ans=s2[x];
		rt=merge(x,y);
		return ans;
	}
	#undef ls
	#undef rs
	double C[maxm];
	inline void Insert(int x,double k){for(;x<=N;x+=x&-x) C[x]+=k;}
	inline double Query(int x){double ans=0;for(;x;x-=x&-x) ans+=C[x];return ans;}
	inline void solve(int *c,int n,seg *b){
		int tot=0;
		for(int i=1;i<=m;i++) q[++tot]=que(b[i].b.x,b[i].b.y-eps,1,i),q[++tot]=que(b[i].a.x,b[i].b.y-eps,-1,i),q[++tot]=que(b[i].b.x,b[i].a.y-eps,-1,i),q[++tot]=que(b[i].a.x,b[i].a.y-eps,1,i);
		sort(q+1,q+1+tot);
		if(eps!=0){
			sort(c+1,c+1+n,[](int x,int y){return a[x].b.x<a[y].b.x;});
			for(int i=1,j=1;i<=tot;i++){
				while(j<=n and a[c[j]].b.x<=q[i].x) Insert(a[c[j]].b.y,a[c[j]].length()),j++;
				ans[q[i].id]+=q[i].tp*Query(q[i].y+eps);
			}
		}
		static pair<int,int> op[maxn<<1];
		for(int i=1;i<=n;i++) op[i*2-1]=make_pair(a[c[i]].a.x,c[i]),op[i*2]=make_pair(a[c[i]].b.x,c[i]);
		n<<=1;
		sort(op+1,op+1+n);
		X=0;
		for(int i=1,j=1;i<=tot;i++){
			while(j<=n and op[j].first<=q[i].x) add(rt,op[j].first-X),X=op[j].first,update(op[j].second),j++;
			add(rt,q[i].x-X),X=q[i].x,ans[q[i].id]+=q[i].tp*query(q[i].y);
		}
		memset(C,0,sizeof C),rt=tot=0;
	}
	inline void solve(){
		solve(c[0],cnt[0],b[0]);
		solve(c[1],cnt[1],b[1]);
	}
	inline void work(){
		srand(time(0));
		n=read();
		double len=0;
		for(int i=1;i<=n;i++){
			a[i].a.x=read(),a[i].a.y=read(),a[i].b.x=read(),a[i].b.y=read();
			if(a[i].a.x>a[i].b.x) swap(a[i].a,a[i].b);
			int t=a[i].a.y>a[i].b.y;
			if(t) a[i].updown();
			c[t][++cnt[t]]=i;
			len+=a[i].length();
		}
		m=read();
		for(int i=1;i<=m;i++) b[0][i].a.x=read(),b[0][i].a.y=read(),b[0][i].b.x=read(),b[0][i].b.y=read(),b[1][i]=b[0][i],b[1][i].updown(),swap(b[1][i].a.y,b[1][i].b.y);
		solve();
		for(int i=1;i<=n;i++) swap(a[i].a.x,a[i].a.y),swap(a[i].b.x,a[i].b.y);
		for(int i=1;i<=m;i++) swap(b[0][i].a.x,b[0][i].a.y),swap(b[0][i].b.x,b[0][i].b.y),swap(b[1][i].a.x,b[1][i].a.y),swap(b[1][i].b.x,b[1][i].b.y);
		eps=0;
		solve();
		for(int i=1;i<=m;i++) printf("%.10f\n",ans[i]/len);
	}
}
signed main(){
	star::work();
	return 0;
}