poj 1171 Picture 线段树

     经典的线段树问题,看了好久才看懂

     解法很简单,按y坐标从小到大,依次扫描每条线段,每次利用线段树记录当前图形在x轴上的投影,然后用这次投影减去上次就是x轴上变化量,而y轴,因为按y轴枚举,只需要用y的差值乘以2再乘以当前线段数即可。

     而线段树的处理就是遇到下边是加入线段树,遇到上边删去及记录当前投影长度,及投影分段情况

     看到网上还有种括号匹配的方法,离散化后枚举,复杂度n^2

/*
author:jxy
lang:C/C++
university:China,Xidian University
**If you need to reprint,please indicate the source**
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 5005
#define lson l,m,rt<<1
#define rson m,r,rt<<1|1
struct Node
{
    int num,len,lnum,lazy; //num为此线段覆盖次数,len为覆盖长度,lnum为段数,lazy为延迟数
    bool lc,rc; //表示线段两端有无被覆盖
};
struct Line
{
    int l,r,y;
    bool up;
};
Line line[maxn<<1];
Node node[maxn<<2];
int num,x[maxn<<1];
void pushup(int rt)//向上更新
{
    node[rt].len=node[rt<<1].len+node[rt<<1|1].len;
    node[rt].lnum=node[rt<<1].lnum+node[rt<<1|1].lnum;
    node[rt].lc=node[rt<<1].lc;
    node[rt].rc=node[rt<<1|1].rc;
    node[rt].num=max(node[rt<<1].num,node[rt<<1|1].num);
    if(node[rt<<1].rc&&node[rt<<1|1].lc) node[rt].lnum--;//如果左孩子的右节点和右孩子的左节点都被标记,则线段数减1
    return;
}
void pushdown(int rt,int l,int r)//向上更新
{
    if(node[rt].lazy)
    {
        int m=l+r>>1;
        node[rt<<1].len=x[m-1]-x[l-1];
        node[rt<<1|1].len=x[r-1]-x[m-1];
        node[rt<<1].num+=node[rt].lazy;
        node[rt<<1|1].num+=node[rt].lazy;
        node[rt<<1].lazy+=node[rt].lazy;
        node[rt<<1|1].lazy+=node[rt].lazy;
        if(node[rt<<1].num>0) node[rt<<1].lnum=node[rt<<1].lc=node[rt<<1].rc=1;
        else node[rt<<1].num=node[rt<<1].len=node[rt<<1].lnum=node[rt<<1].lc=node[rt<<1].rc=0; //如果覆盖数为0,则删除
        if(node[rt<<1|1].num>0) node[rt<<1|1].lnum=node[rt<<1|1].lc=node[rt<<1|1].rc=1;
        else node[rt<<1|1].num=node[rt<<1|1].len=node[rt<<1|1].lnum=node[rt<<1|1].lc=node[rt<<1|1].rc=0;
    }
    node[rt].lazy=0;
}
void add(int L,int R,int l,int r,int rt)//增加线段
{
    if(L<=l&&R>=r)
    {
        node[rt].len=x[r-1]-x[l-1];
        node[rt].num++;
        node[rt].lc=node[rt].rc=node[rt].lnum=1;
        node[rt].lazy++;//延迟更新
        return;
    }
    pushdown(rt,l,r);
    int m=(l+r)>>1;
    if(L<m)add(L,R,lson);
    if(R>m)add(L,R,rson);
    pushup(rt);
    return;
}
void del(int L,int R,int l,int r,int rt)//删除线段
{
    if(L<=l&&R>=r)
    {
        node[rt].num--;
        if(node[rt].num<=0)
        {
            node[rt].len=0;
            node[rt].lc=node[rt].rc=node[rt].lnum=0;
            node[rt].num=0;
            node[rt].lazy--;//如果此线段被删除,延迟更新子线段
            return;
        }
        if(r-l==1)return;
    }
    pushdown(rt,l,r);
    int m=(l+r)>>1;
    if(L<m)del(L,R,lson);
    if(R>m)del(L,R,rson);
    pushup(rt);
    return;
}
bool cmp(Line a,Line b)
{
    if(a.y==b.y) return a.l<b.l;
    else return a.y<b.y;
}
inline int find(int key)//查询x坐标对应的下标,因为线段树要求从1开始,所以下标加1
{
    return lower_bound(x,x+num,key)-x+1;
}
int main()
{
    int n,i,x1,x2,y1,y2;
    while(~scanf("%d",&n))
    {
        memset(node,0,sizeof(node));
    num=0;
    for(i=0;i<n;i++)
    {
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        x[num++]=x1;x[num++]=x2;
        line[i<<1].l=x1;line[i<<1].r=x2;line[i<<1].y=y1;line[i<<1].up=1;
        line[i<<1|1].l=x1;line[i<<1|1].r=x2;line[i<<1|1].y=y2;line[i<<1|1].up=0;
    }
    sort(x,x+num);//对x排序,离散化处理
    num=unique(x,x+num)-x;//去重
    n=n<<1;
    sort(line,line+n,cmp);
    int pre=0,ans=0;
    for(i=0;i<n-1;i++)
    {
        if(line[i].up)//如果是下边,则加边
            add(find(line[i].l),find(line[i].r),1,num,1);
        else //如果是上边,则删除
            del(find(line[i].l),find(line[i].r),1,num,1);
        ans+=node[1].lnum*(line[i+1].y-line[i].y)*2;//平行y轴
        ans+=abs(node[1].len-pre);//平行x轴
        pre=node[1].len;
    }
    del(find(line[i].l),find(line[i].r),1,num,1);
    ans+=abs(node[1].len-pre);
    printf("%d\n",ans);
    }
}
时间: 2024-10-25 14:07:38

poj 1171 Picture 线段树的相关文章

poj 2777(线段树的节点更新策略)

/* 之前的思想是用回溯的方式进行颜色的更新的!如果用回溯的方法的话,就是将每一个节点的颜色都要更新 通过子节点的颜色情况来判断父节点的颜色情况 !这就是TLE的原因! 后来想一想没有必要 !加入[a, b] 区间有p管辖,那么tree[p]的颜色值就是[a, b]所有点的颜色值! 如果[a,b]的子区间[c,d]没被跟新,那么tree[p]也是[c,d]的值! 否则,在更新[c,d]区间的时候,一定会经过 p 点!然后由上到下更新p<<1 和 p<<1|1 的值! 当找到[c,d

poj 2528 Mayor&#039;s posters(线段树+离散化)

/* poj 2528 Mayor's posters 线段树 + 离散化 离散化的理解: 给你一系列的正整数, 例如 1, 4 , 100, 1000000000, 如果利用线段树求解的话,很明显 会导致内存的耗尽.所以我们做一个映射关系,将范围很大的数据映射到范围很小的数据上 1---->1 4----->2 100----->3 1000000000----->4 这样就会减少内存一些不必要的消耗 建立好映射关系了,接着就是利用线段树求解 */ #include<ios

POJ 3468 线段树 区间更新区间查询

题意:给出一段数列,任意区间加上一个整数v,任意区间查询.给出N个数,Q次操作. 线段树的题,延迟指针不更新到底就行了. #include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 100005 long long sum[maxn<<2],col[maxn<<2]; void

【转】POJ 3264 线段树解法

每个算法都是数学家或者计算机学家多年研究的结果,不是我自己臆造的,所以学习一个新算法的最佳方法还是看写的好的代码. 按照惯例,我就粘贴一个网上写的很好的帖子... 原文地址:http://ip96cns.blog.163.com/blog/static/17009519220112525029459/ 线段树的查找:(感谢高手指点,虽然只是一点却让我把线段树的内容理顺了)这里是我的一些总结吧           一个线段树的结点表示一个区间,同时结点中保存所需要的信息.          如果询

POJ3468&amp;#160;线段树-模板题

Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval. In

线段树和RMQ解析和模板

这几天在看RMQ的题目,但是很多RMQ的题目也可以用线段树解决... 看来两者之间有很多关系,那就都好好看吧...下面先贴出一个大牛对两者的解释.. RMQ (Range Minimum/Maximum Query)问题是指: 对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在[i,j]里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题  主要方法及复杂度(处理复杂度和查询复杂度)如下:  1.朴素(即搜索) O(n)-O(n)  2.线段树(se

线段树(Segment Tree)

1.概述 线段树,也叫区间树,是一个完全二叉树,它在各个节点保存一条线段(即"子数组"),因而常用于解决数列维护问题,基本能保证每个操作的复杂度为O(lgN). 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b].因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度. 使用线段树可以

hdu 4614 Vases and Flowers 线段树

    比赛最后40分钟写的,线段树加二分,思路写法完全没问题,但最后提交了2次都WA了,回来后发现是模板更新lazy把0当做更新过的了,但是应该是1 /* author:jxy lang:C/C++ university:China,Xidian University **If you need to reprint,please indicate the source** */ #include <iostream> #include <cstdio> #include <

c++-帮我看一下这段线段树的代码应该怎么用?有问题吗?

问题描述 帮我看一下这段线段树的代码应该怎么用?有问题吗? #include#define MAXN 10005struct node{ int left; int right; int mid; int cover;};node SegTree[3*MAXN];void BuildSegTree(int lint rint num){ SegTree[num].left=l; SegTree[num].right=r; SegTree[num].mid=(l+r)/2; if(l+1!=r)