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}}\ ...
随机推荐
- JUC 并发编程--11, AQS源码原理解析, ReentrantLock 源码解读
这里引用别人博客,不重复造轮子 https://blog.csdn.net/u012881584/article/details/105886486 https://www.cnblogs.com/w ...
- P4779 【模板】单源最短路径(标准版)单源最短路Dijkstra
题目描述 给定一个$n$个点,$m$条有向边的带非负权图,请你计算从$s$出发,到每个点的距离. 数据保证你能从$s$出发到任意点. 输入格式 第一行为三个正整数$n,m,s$. 第二行起$m$行,每 ...
- 生成树协议(STP)
一.交换网络环路的产生 1.广播风暴的形成 2.多帧复制 3.MAC地址表紊乱 二.STP简介 STP-Spanning Tree Protocol(生成树协议) 逻辑上断开环路,防止广播风暴的产生 ...
- 身为一枚优秀的程序员必备的基于Redis的分布式锁和Redlock算法
1 前言 今天开始来和大家一起学习一下Redis实际应用篇,会写几个Redis的常见应用. 在我看来Redis最为典型的应用就是作为分布式缓存系统,其他的一些应用本质上并不是杀手锏功能,是基于Redi ...
- 【题解】codeforces 219D Choosing Capital for Treeland 树型dp
题目描述 Treeland国有n个城市,这n个城市连成了一颗树,有n-1条道路连接了所有城市.每条道路只能单向通行.现在政府需要决定选择哪个城市为首都.假如城市i成为了首都,那么为了使首都能到达任意一 ...
- 手写Spring,定义标记类型Aware接口,实现感知容器对象
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 同事写的代码,我竟丝毫看不懂! 大佬的代码,就像 "赖蛤蟆泡青蛙,张的丑玩 ...
- Golang中的各种时间操作
Golang中的各种时间操作 需求 时间格式的转换比较麻烦,自己写了个工具,可以通过工具中的这些方法相互调用转成自己想要的格式,代码如下,后续有新的函数再添加 实现代码 package utils i ...
- 10、linux启动过程
(1)linux启动说明: 第一步:开机自检,检查硬件,加载BIOS(帮我们找到启动盘是谁): 第二步:读取MBR(读取启动硬盘0柱面0磁道1扇区(512字节)的前446字节,找到装有操作系统的分区) ...
- 【PC桌面软件的末日,手机移动端App称王】写在windows11支持安卓,macOS支持ios,龙芯支持x86和arm指令翻译
面对这场突如其来的变革,作为软件开发者,应该如何选择自己今后的发展方向?桌面软件开发领域还有前景吗? 起源 自从苹果发布m1处理器,让自家Mac支持IOS移动端app运行之后,彻底打破了移动端app和 ...
- @EnableDiscoveryClient与Nacos自动注册
前一阵看到有篇博客说cloud从Edgware版本开始,可以不加@EnableDiscoveryClient注解,只要配置好注册中心的相关配置即可自动开启服务注册功能,比较好奇其中的原理,研究了一番特 ...