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;
}
P6106 [Ynoi2010] Self Adjusting Top Tree的更多相关文章
- 动态树(LCT、Top Tree、ETT)
LCT Upd: 一个细节:假如我们要修改某个节点的数据,那么要先把它makeroot再修改,改完之后pushup. LCT是一种维护森林的数据结构,本质是用Splay维护实链剖分. 实链剖分大概是这 ...
- Size Balance Tree(SBT模板整理)
/* * tree[x].left 表示以 x 为节点的左儿子 * tree[x].right 表示以 x 为节点的右儿子 * tree[x].size 表示以 x 为根的节点的个数(大小) */ s ...
- [bzoj3282]Tree (lct)
昨天看了一天的lct..当然幸好最后看懂了(也许吧..) 论善良学长的重要性T_T,老司机带带我! 这题主要是删边的时候还要判断一下..蒟蒻一开始天真的以为存在的边才能删结果吃了一发wa... 事实是 ...
- Link-Cut Tree指针模板
模板: 以下为弹飞绵羊代码: #define Troy #include "bits/stdc++.h" using namespace std; ; inline int rea ...
- 【BZOJ2631】tree
Description 一棵n个点的树.每一个点的初始权值为1. 对于这棵树有q个操作,每一个操作为下面四种操作之中的一个: + u v c:将u到v的路径上的点的权值都加上自然数c: - u1 v1 ...
- PAT 1086 Tree Traversals Again[中序转后序][难]
1086 Tree Traversals Again(25 分) An inorder binary tree traversal can be implemented in a non-recurs ...
- LeetCode Construct Binary Tree from String
原题链接在这里:https://leetcode.com/problems/construct-binary-tree-from-string/description/ 题目: You need to ...
- HDU 6191 Query on A Tree(字典树+离线)
Query on A Tree Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 132768/132768 K (Java/Othe ...
- 【置顶】Trotyl's OI tree
\(\rm thx\):@\(\rm UntilMadow\) ! \(\color{Green}{\rm Pupil}\) :只会一点点 \(\color{blue}{\text{Expert}}\ ...
随机推荐
- 深入理解java虚拟机笔记Chapter12
(本节笔记的线程收录在线程/并发相关的笔记中,未在此处提及) Java内存模型 Java 内存模型主要由以下三部分构成:1 个主内存.n 个线程.n 个工作内存(与线程一一对应) 主内存与工作内存 J ...
- linux远程和软件包的管理
远程管理 ssh 用户名@对方IP地址 -X 在本地可以运行对方的图形程序 端口 22 [root@room9pc01 ~]# ssh root@172.25.0.11 [root@serve ...
- 【NX二次开发】Block UI 线型
属性说明 常规 类型 描述 BlockID String 控件ID Enable Logical 是否可操作 Group Logical ...
- [Abp vNext 源码分析] - 20. 电子邮件与短信支持
一.简介 ABP vNext 使用 Volo.Abp.Sms 包和 Volo.Abp.Emailing 包将短信和电子邮件作为基础设施进行了抽象,开发人员仅需要在使用的时候注入 ISmsSender ...
- 在vue中下拉框切换事件中改新建表单中的一个值,页面不显示
事件中改新建表单中的一个值,页面不显示,当另一个对象值发生改变时,这个页面上的值才会显示 由于新建表单是弹窗,在弹出时会重新给每个字段重新赋值,在赋值时没给这个字段赋值(常见新加功能时,加了一个字段, ...
- Ansible自动化运维应用实战
实验环境 centos7.4 主机名称 IP 配置 用途 controlnode 172.16.1.120 1核/1G/60G ansible slavenode1 172.16.1.121 1核/1 ...
- 26、linux下安装MongoDB
26.1.MongoDB介绍: 1.什么是MongoDB: MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统.在高负载的情况下,添加更多的节点,可以保证服务器性能. Mo ...
- docker部署的nginx非80端口无法访问
请检查nginx容器是否只开启了80端口映射!!! 请检查nginx容器是否只开启了80端口映射!!! 请检查nginx容器是否只开启了80端口映射!!! 如果你访问的端口在nignx容器已经开启了端 ...
- SpringBoot拦截器及源码分析
1.拦截器是什么 java里的拦截器(Interceptor)是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止 ...
- 深入理解 SynchronizationContext
深入理解 SynchronizationContext 目录 深入理解 SynchronizationContext SynchronizationContext(后续以SC简称) 是什么? 1.1 ...