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
",ans[i]/len);
}
}
signed main(){
star::work();
return 0;
}