线段树——扫描线

PKU 1151 && hdu1542  Atlantis  矩形面积并

http://poj.org/problem?id=1151

题意:

给出n个矩形,每个矩形给出左下角坐标,右上角坐标。然后求矩形并的总面积;

思路:

 浮点数先要离散化;然后把矩形分成两条边,上边和下边,对横轴建树,然后从下到上扫描上去,用cnt表示该区间下边比上边多几个,sum代表该区间内被覆盖的线段的长度总和
这里线段树的一个结点并非是线段的一个端点,而是该端点和下一个端点间的线段,所以题目中r+1,r-1的地方可以自己好好的琢磨一下;

这里点到线段的转化不好理解:

我们的线段树的叶子节点表示的是线段,座椅如果我们求1到4的线段长度是实际上是求的1,2,3好线段,所以r要-1,而当我们真正求长度是是X[i + 1] - X[i]才是i线段的长度。

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a , b) ((a) < (b) ? (a) : (b))
#define Max(a , b) ((a) > (b) ? (a) : (b))

#define ll __int64
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 100007
#define N 227
using namespace std;
//freopen("din.txt","r",stdin);

struct Seg{
    double l,r,h;
    int mk;
    Seg(){}
    Seg(double L,double R,double H,int MK){
        l = L; r = R; h = H; mk = MK;
    }
    bool operator < (const Seg &tmp) const{
        return h < tmp.h;
    }
}seg[N];

int cnt[N<<2];
double sum[N<<2],X[N];

void pushup(int rt,int l,int r){
    if (cnt[rt]) sum[rt] = X[r + 1] - X[l];//下边比上边多此区间仍满足条件
    else if (l == r) sum[rt] = 0;//单个的一条线段且x长度不满足条件肯定为0
    else sum[rt] = sum[rt<<1] + sum[rt<<1|1];//x不满足条件但其子树可能存在满足条件的
}
void update(int L,int R,int sc,int l,int r,int rt){
    if (L <= l && r <= R){
        cnt[rt] += sc;//更新
        pushup(rt,l,r);
        return ;
    }
    int m = (l + r)>>1;
    if (L <= m) update(L,R,sc,lc);
    if (m < R) update(L,R,sc,rc);
    pushup(rt,l,r);
}
//离散化后二分查找
int bSearch(double val,int n){
    int l = 0;
    int r = n;
    while (l <= r){
        int m = (l + r)>>1;
        if (val == X[m]) return m;
        else if (val < X[m]) r = m - 1;
        else l = m + 1;
    }
    return l;
}
int main(){
    //freopen("din.txt","r",stdin);
    int i;
    int n;
    int cas = 1;
    double x1,y1,x2,y2;
    while (~scanf("%d",&n)){
        if (!n) break;
        int len = 0;
        //按x轴建树,记录横向线段以及x左边
        for (i = 0; i < n; ++i){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            X[len] = x1;
            seg[len++] = Seg(x1,x2,y1,1);
            X[len] = x2;
            seg[len++] = Seg(x1,x2,y2,-1);
        }
        
        //离散化
        sort(X,X + len);
        sort(seg,seg + len);
        int k = 1;
        for (i = 1; i < len; ++i){
            if (X[i] != X[i - 1]) X[k++] = X[i];
        }
        //线段树扫描处理
        double res = 0;
        CL(sum, 0); CL(cnt,0);
        for (i = 0; i < len - 1; ++i){
            int l = bSearch(seg[i].l,k - 1);
            int r = bSearch(seg[i].r,k - 1) - 1;//这里讲点转化成线段(难理解)
            if (l <= r) update(l,r,seg[i].mk,0,k - 2,1);
            res += sum[1]*(seg[i + 1].h - seg[i].h);//sum[1]记录整个区间满足条件的x的长度
        }
        printf("Test case #%d\n",cas++);
        printf("Total explored area: %.2lf\n\n",res);
    }
    return 0;
}

 

pku 1177 Picture 矩形周长并

 http://poj.org/problem?id=1177 

题意:

给出n个矩形,每个矩形给出左下角坐标,右上角坐标。然后求矩形周长的并;

思路:

扫描线,按x轴建树,len[]记录横向有效长度,numseg[]记录叔侄方向的有效个数。然后就是扫描一遍过去。

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a , b) ((a) < (b) ? (a) : (b))
#define Max(a , b) ((a) > (b) ? (a) : (b))

#define ll __int64
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 10007
#define N 20007
using namespace std;
//freopen("din.txt","r",stdin);

struct Seg{
    int l,r,h;
    int mk;
    Seg(){}
    Seg(int L,int R,int H,int MK){
        l = L; r = R; h = H; mk = MK;
    }
    bool operator < (const Seg &tmp) const{
        return h < tmp.h;
    }
}seg[M];

int len[N<<2],cnt[N<<2],numseg[N<<2];
int lbd[N<<2],rbd[N<<2];

void pushup(int rt,int l,int r){
    if (cnt[rt]){
        len[rt] = r - l + 1;
        lbd[rt] = rbd[rt] = 1;//标记边界
        numseg[rt] = 2;
    }
    else if (l == r) len[rt] = lbd[rt] = rbd[rt] = numseg[rt] = 0;
    else{
        len[rt] = len[rt<<1] + len[rt<<1|1];
        numseg[rt] = numseg[rt<<1] + numseg[rt<<1|1];
        lbd[rt] = lbd[rt<<1];
        rbd[rt] = rbd[rt<<1|1];
        if (lbd[rt<<1|1] && rbd[rt<<1]) numseg[rt] -= 2;//边界重合重合
    }
}
void update(int L,int R,int sc,int l,int r,int rt){
    if (L <= l && r <= R){
        cnt[rt] += sc;
        pushup(rt,l,r);
        return ;
    }
    int m = (l + r)>>1;
    if (L <= m) update(L,R,sc,lc);
    if (m < R) update(L,R,sc,rc);
    pushup(rt,l,r);
}
int main(){
   //freopen("din.txt","r",stdin);
    int n,i;
    int x1,y1,x2,y2;
    scanf("%d",&n);
    int m = 0;
    int L = 10001,R = -10001;
    for (i = 0; i < n; ++i){
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        L = min(L,x1);//求出边界
        R = max(R,x2);
        seg[m++] = Seg(x1,x2,y1,1);
        seg[m++] = Seg(x1,x2,y2,-1);
    }
    sort(seg,seg + m);
    int res = 0;
    int last = 0;
    CL(cnt,0); CL(numseg,0); CL(len,0);
    CL(lbd,0); CL(rbd,0);
    //扫描
    for (i = 0; i < m; ++i){
        if (seg[i].l < seg[i].r)
        update(seg[i].l,seg[i].r - 1,seg[i].mk,L,R - 1,1);
        res += numseg[1]*(seg[i + 1].h - seg[i].h);//竖直方向上有效长度
        res += iabs(len[1] - last);//水平方向上的长度
        last = len[1];//last记录上次的横向的有效长度
    }
    printf("%d\n",res);
    return 0;
}

 

 

 

 

(1)普通用户端(全平台) 音乐播放核心体验: 个性化首页:基于 “听歌历史 + 收藏偏好” 展示 “推荐歌单(每日 30 首)、新歌速递、相似曲风推荐”,支持按 “场景(通勤 / 学习 / 运动)” 切换推荐维度。 播放页功能:支持 “无损音质切换、倍速播放(0.5x-2.0x)、定时关闭、歌词逐句滚动”,提供 “沉浸式全屏模式”(隐藏冗余控件,突出歌词与专辑封面)。 多端同步:自动同步 “播放进度、收藏列表、歌单” 至所有登录设备(如手机暂停后,电脑端打开可继续播放)。 音乐发现与管理: 智能搜索:支持 “歌曲名 / 歌手 / 歌词片段” 搜索,提供 “模糊匹配(如输入‘晴天’联想‘周杰伦 - 晴天’)、热门搜索词推荐”,结果按 “热度 / 匹配度” 排序。 歌单管理:创建 “公开 / 私有 / 加密” 歌单,支持 “批量添加歌曲、拖拽排序、一键分享到社交平台”,系统自动生成 “歌单封面(基于歌曲风格配色)”。 音乐分类浏览:按 “曲风(流行 / 摇滚 / 古典)、语言(国语 / 英语 / 日语)、年代(80 后经典 / 2023 新歌)” 分层浏览,每个分类页展示 “TOP50 榜单”。 社交互动功能: 动态广场:查看 “关注的用户 / 音乐人发布的动态(如‘分享新歌感受’)、好友正在听的歌曲”,支持 “点赞 / 评论 / 转发”,可直接点击动态中的歌曲播放。 听歌排行:个人页展示 “本周听歌 TOP10、累计听歌时长”,平台定期生成 “全球 / 好友榜”(如 “好友中你本周听歌时长排名第 3”)。 音乐圈:加入 “特定曲风圈子(如‘古典音乐爱好者’)”,参与 “话题讨论(如‘你心中最经典的钢琴曲’)、线上歌单共创”。 (2)音乐人端(创作者中心) 作品管理: 音乐上传:支持 “无损音频(FLAC/WAV)+ 歌词文件(LRC)+ 专辑封面” 上传,填写 “歌曲信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值