首页 > 其他分享 >写个扫描线吧

写个扫描线吧

时间:2023-09-21 22:44:05浏览次数:36  
标签:写个 ll mid len 扫描线 区间 lazytag 线段

虽然扫描线很简单,但是我发现理解还是有些不清晰,于是决定写一篇。
原问题,或者说是例题

一个不好暴力东西,暴力模拟不带优化至少O(n^3)
也是一个非常经典的线段树例题
(因为懒得画图所以下面就直接用网上dalao的题解里面的图片了)
首先,因为我们要在线段树上面记录下来每一个起始点和结束点,所以离散化是必须的

下面是一个O(n^2)暴力
我们可以建立一个c[i]数组,里面是i这个点被覆盖的长度,然后用一个len数组来记录一下一共有多少地方被覆盖过
这个len是用线段树维护的
到这里就比较明显了,对于这个c[i],我们离散化之后其实就是对它进行一个区间修改和区间查询,那就是线段是的例题了。
最终就是一个O(nlogn)

 

然后一些细节,因为这个题目算是加深了我对于lazytag的理解
我写的时候刚开始没有感觉,然后仔细想想发现对于这个len我没法用lazytag维护(这个其实就是对于lazytag的理解
lazytag是在每一次查询的时候发挥作用,如果这个区间刚刚好完全被包含在内,那么就不需要继续往下递归,而是直接返还已经算好的值
而对于不是完全被包含的区间,则是在递归前把它的祖宗节点的lazytag传给他的儿子节点,这样lazytag就成功的被处理了。
而这个为什么不能用lazytag就是因为这个,我们要记录的内容是:这个区间里面有几个点被标记过
但是,对于一个区间,它被lazytag标记前我们不知道里面有几个节点被标记了,所以我们也不知道里面有几个点会在被改变后给len贡献值(就是不知道有几个点会从0变成1)。
lazytag能做的事情有一个特点:他能够在一个区间的层面上统计一个修改能够对我们需要的答案造成什么影响。
就比如,lazytag能用在区间加和区间查询数字和上,因为一个区间加上一个数字我们可以用这个数字*区间长度来计算他们的和的变化。
但是这个不行。
所以我们对于每一次修改都要从一个我们能统计的地方开始统计有几个点被覆盖过,然后把答案上传,汇在一起。
这个过程就直接做就好了,大概就是 如果有一个区间,他们是集体有值的,那就给这个区间的len+=r-l+1,如果不是,那就让这个点的len等于它左儿子的len+右儿子的len
这也是我线段树经常想糊涂的地方。
希望我和看这篇博客的人现在和以后都不会再想不清楚了吧

然后这个题目有一个要注意的点就是,我们线段树上面的每一个点,其实对应的是一个区间,而不是一个点,所以写的时候有些部分就要变化

##代码

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read() {
    char c=getchar();ll a=0,b=1;
    for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
    for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
ll n,totx,disx[4000001],disy[4000001],toty,sum[4000001];
ll disx2[4000001],totx2,disy2[4000001],toty2,ans,len[4000001];
struct node
{
    ll x,y,a,b,k;
}e[4000001],e1[4000001];
bool cmp(node a,node b)
{
    return a.x<b.x;
}
ll ery(ll x)//没用 
{
    ll l=1,r=toty2;
    while(l<=r)
    {
        ll mid=(l+r)/2;
        if(disy2[mid]==x)return mid;
        if(disy2[mid]>x)r=mid-1;
        else l=mid+1;
    }
    return l;
}
inline ll ls(ll x){
    return x*2;
}
inline ll rs(ll x){
    return x*2+1;
}
inline void push_up(ll p,ll l,ll r)
{
    if(sum[p])
        len[p]=disy2[r+1]-disy2[l];
    else 
        len[p]=len[ls(p)]+len[rs(p)];
}
void build(ll l,ll r,ll p)
{
    sum[p]=0;len[p]=0;
    if(l==r)return;
    ll mid=(l+r)/2;
    build(l,mid,ls(p));
    build(mid+1,r,rs(p));
    push_up(p,l,r);
}
void add(ll tl,ll tr,ll l,ll r,ll p,ll k)
{
    if(r+1<=tl||tr<=l)return;
    if(tl<=l&&tr>=r+1)
    {
        sum[p]+=k;
        push_up(p,l,r);
        return;
    }
    ll mid=(l+r)/2;
    if(tl<=mid)add(tl,tr,l,mid,ls(p),k);
    if(mid<tr)add(tl,tr,mid+1,r,rs(p),k);
    push_up(p,l,r);
}
int main()
{
    n=read();
    for(ll i=1;i<=n;i++)
    {
        e[i].x=read();e[i].y=read();
        e[i].a=read();e[i].b=read();
        e1[i].x=e[i].x;  e1[i].y=e[i].y;  e1[i].b=e[i].b;  e1[i].k=1;
        e1[i+n].x=e[i].a;e1[i+n].y=e[i].y;e1[i+n].b=e[i].b;e1[i+n].k=-1;
        disy[++toty]=e[i].y;//x的离散化没用 
        disy[++toty]=e[i].b;
    }
    sort(disy+1,disy+toty+1);disy[0]=-1;
    for(ll i=1;i<=toty;i++)
        if(disy[i]!=disy[i-1])
            disy2[++toty2]=disy[i];//离散化 
    sort(e1+1,e1+1+n*2,cmp);
    build(1,toty2,1);
    for(ll i=1;i<n*2;i++)
    {
        add(ery(e1[i].y),ery(e1[i].b),1,toty2,1,e1[i].k);
        ans+=len[1]*(e1[i+1].x-e1[i].x);
    }
    cout<<ans<<endl;
    return 0;
}

 

下面是poj2482,一个扫描线的应用

代码

标签:写个,ll,mid,len,扫描线,区间,lazytag,线段
From: https://www.cnblogs.com/HLZZPawa/p/17716758.html

相关文章

  • 写个for循环
    #include<stdio.h>intmain(){ intsum=0; intsum1=0; for(intnum=1;num<=100;num+=2){ sum+=num; printf("奇数和为%d\n",sum); } for(intnum1=2;num1<=100;num1+=2){ sum1+=num1; printf("偶数和为%d\......
  • 扫描线
    #include<bits/stdc++.h>#definemaxn200005#defineintlonglongusingnamespacestd;intx[maxn];//x[i]表示从左往右第i条竖边inttsum[maxn<<3],tlen[maxn<<3];//tsum表示当前节点被覆盖的次数//tlen是当前节点所对应的长方形的长voidpushup(intk,intl,intr)/......
  • 无聊写个微信步数 (我老妈总是查我的微信步数)
    思路先找到提交步数的接口 (我老妈总是查我的微信步数)这里我网上随便找了一个 https://apis.jxcxin.cn/doc/mi.html 挺好用的第二步就是获取当前步数了(个人技术原因不能直接在) 只能依靠小程序 抓到请求接口 当然是有加密啦逆向代码 核心扣取下来分析很......
  • 扫描线补充
    1.两条扫描线之间,不一定是一个矩形,可能是多个不相交的,高相同的矩形2.扫描线的板子没有pushdownvoidupd(intu,intl,intr){ if(cnt[u]){ len[u]=b[r+1]-b[l]; sum[u]=2; lh[u]=rh[u]=1; }else{ len[u]=len[lch]+len[rch]; sum[u]=sum[lch]+sum[rch]; lh[u]=lh[......
  • pdfjs-dist v2.11.338写个react demo
    app.jsximport'./App.css'import*aspdfjsfrom"pdfjs-dist";import"pdfjs-dist/web/pdf_viewer.css";import{useEffect,useRef,useState}from'react'import{PDFViewer,PDFLinkService,EventBus}from'p......
  • P5490 【模板】扫描线
    \(P5490\)【模板】扫描线一、题目描述求\(n\)个四边平行于坐标轴的矩形的面积并。输入格式第一行一个正整数\(n\)。接下来\(n\)行每行四个非负整数\(x_1,y_1,x_2,y_2\),表示一个矩形的四个端点坐标为\((x_1,y_1),(x_1,y_2),(x_2,y_2),(x_2,y_1)\)。输出格式......
  • 【单调队列】 单调队列的“扫描线”理解
    【单调队列】单调队列的“扫描线”理解  “如果一个选手比你小还比你强,你就可以退役了。”——单调队列的原理比你强,而且比你影响时间更长。某种意义上,数学思维是生活中的思考的延伸。  算法学习笔记(66):单调队列。引用Pecco的算法笔记。  在这里给出一种扫描线......
  • 扫描线学习笔记
    0.写在前面扫描线好闪,拜谢扫描线1.问题的引入在一个二维的坐标系上,给出多个矩形,求他们的面积并2.问题的分析假设我们有这么一张图你要求这三个矩形的面积并,可以考虑容斥原理,但这样会TLE但总之,他最终的结果是围成了一个多边形那你不妨考虑,重新分割这个最终的图形那......
  • 「学习笔记」扫描线
    什么是扫描线?顾名思义,一根用来扫描的线扫描线就是一条线在整个图上扫来扫去,它一般被用来解决图形面积,周长,以及二维数点等问题。下面我们用例题来引入。P5490【模板】扫描线-洛谷|计算机科学教育新生态(luogu.com.cn)我们对于这种题有三种做法暴力的进行覆盖扫描容......
  • 为了成为Java大牛,我决定手写个JVM~
    JVM对我们很多人来说就像个黑盒子,无从下手,但是又是我们JavaCoder不得不去深入研究的一门技术国内玩JVM的大牛很少,知名的就那么几个,而玩好JVM又教好JVM的人更是少之又少。今天给大家介绍其中一位,江湖人送外号道格牙的子牙老师。下面的时间,交给他。哈喽,我就是江湖人送外号[......