UVA10587 Mayor's posters【线段树】

在这里插入图片描述

在这里插入图片描述

translate

按照顺序向一面墙上贴n张海报,海报会互相覆盖,求最后可以看得到的海报张数

analysis

别样的线段树
要维护的是某个区间颜色的种类数
但这个题是所有操作完后才进行询问,所有可以只维护区间左边的颜色种类和右边的颜色种类,在最后的时候对于每一个长度为1的区间进行一次询问,询问其颜色种类即可
由于大区间的l,r和小区间的l,r有关系,所以可以维护(其实区间颜色个数好像也可以维护的)
但本题还不止这些

  • 离散化
    海报长度涉及区间长达1e7,你确定要开一个这么长的线段树?
    显然需要离散化
    离散化的思路也很简单,就是去掉没用的信息(空间):
    区间[1,100],[23,131414]
    直接离散成:
    [1,3],[2,4]
    代码实现需要用到unique函数和lowerbound函数,网上到处都是对于这两个函数的资料

但是这道题的离散化还有所不同,由于这里的x,代表的不是一个点,而是第x段长度为1的区间,所以,为了避免离散化的时候将本不相邻的两个区间([1,2][5,6])离散成相邻区间([1,2][3,4]),需要将每个区间右端点+1也加入离散化的临时数组中,具体原理,动手模拟即可理解

附上离散化代码:

int lssz[20005 << 4];//离散数组(l s s z)
int cnt=0;
inline int lisan(){
    cnt=0;
	loop(i,1,n){
        lssz[++cnt]=poster[i].l;
        lssz[++cnt]=poster[i].r;
        lssz[++cnt]=poster[i].r+1;
    }//将要离散的数据放入临时数组中
    sort(lssz+1,lssz+1+cnt);
    int len=unique(lssz+1,lssz+1+cnt)-lssz-1;//排序后去重
    loop(i,1,n){
        poster[i].l=lower_bound(lssz+1,lssz+1+len,poster[i].l)-lssz;
        poster[i].r=lower_bound(lssz+1,lssz+1+len,poster[i].r)-lssz;
    }//用下标来代替原数
    return len;
}
  • 初始化

本题多组数据!记得初始化!
就是因为没注意到这点,写对拍的时候都忘了写多组数据,RE了无数遍

code

#include <iostream>
#include <fstream>
#include <cstdio>
#include <cmath>
#include <map>
#include <set>
#include <bitset>
#include <ctime>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <vector>
#include <list>
using namespace std;
#define loop(i,start,end) for(int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(int i=start;i>=end;--i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define ll long long
template<typename T>void read(T &x){
    x=0;char r=getchar();T neg=1;
    while(r>'9'||r<'0'){if(r=='-')neg=-1;r=getchar();}
    while(r>='0'&&r<='9'){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
    x*=neg;
}

const int maxn=10000+10;
int T;
int n;

struct node{
    int l;
    int r;
}poster[maxn];

int lssz[20005 << 4];
int cnt=0;
inline int lisan(){
    cnt=0;//初始化!! 
	loop(i,1,n){
        lssz[++cnt]=poster[i].l;
        lssz[++cnt]=poster[i].r;
        lssz[++cnt]=poster[i].r+1;
    }
    sort(lssz+1,lssz+1+cnt);
    int len=unique(lssz+1,lssz+1+cnt)-lssz-1;//?-1
    loop(i,1,n){
        poster[i].l=lower_bound(lssz+1,lssz+1+len,poster[i].l)-lssz;
        poster[i].r=lower_bound(lssz+1,lssz+1+len,poster[i].r)-lssz;
    }
    return len;
}

int lef[maxn<<4];
int rig[maxn<<4];
int lazy[maxn<<4];
int nfc=0;

inline void pushup(int rt){
    lef[rt]=lef[rt<<1];
    rig[rt]=rig[rt<<1|1];
}

inline void pushdown(int rt){
    if(!lazy[rt])
        return;
    lef[rt<<1]=lef[rt];
    rig[rt<<1]=rig[rt];
    lef[rt<<1|1]=lef[rt];
    rig[rt<<1|1]=rig[rt];
    lazy[rt<<1]=lazy[rt];
    lazy[rt<<1|1]=lazy[rt];
    lazy[rt]=0;
}

void update(int l,int r,int nl,int nr,int rt){
    if(l<=nl&&nr<=r){
        lazy[rt]=nfc;
        lef[rt]=nfc;
        rig[rt]=nfc;
        return;
    }
    pushdown(rt);
    int mid=(nl+nr)>>1;
    if(mid>=l){
        update(l,r,nl,mid,rt<<1);
    }
    if(mid<r){
        update(l,r,mid+1,nr,rt<<1|1);
    }
    pushup(rt);
}

int vis[20005 << 4];

void getres(int l,int r,int rt){
    if(l==r){
        vis[lef[rt]]=1;
        return;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    getres(l,mid,rt<<1);
    getres(mid+1,r,rt<<1|1);
}

void debug(int x){

}

int main(){
	read(T);
    while(T--){
        read(n);
        loop(i,1,n){
            read(poster[i].l);
            read(poster[i].r);
        }
        
        int x=lisan();
        nfc=0; 
        clean(lef,0);
        clean(rig,0);
        clean(lazy,0);
        loop(i,1,n){
			++nfc;
			update(poster[i].l,poster[i].r,1,x,1);
        }
        
        clean(vis,0);
		getres(1,x,1);
        int res=0;
        loop(i,1,x){
            if(vis[i])
                ++res;
        }
        printf("%d\n",res);

    }
    return 0;
}

(线段树标记永久化模板)
在这里插入图片描述


update 2019.9.1
上面那种做法被mzoj上的玄学数据卡了…

但是还有另外一种做法,就是倒序模拟每个海报的覆盖情况,如果当前海报覆盖的区间有一部分没有没覆盖,那么就说明它可以被看到

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define ll long long
using namespace std; 

template <typename T> void in(T &x) {
    x = 0; T f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
    while( isdigit(ch)) {x = 10 * x + ch - 48; ch = getchar();}
    x *= f;
}

template <typename T> void out(T x) {
    if(x < 0) x = -x , putchar('-');
    if(x > 9) out(x/10);
    putchar(x%10 + 48);
}
//-------------------------------------------------------
const int N = 40005,M = 10001;
int T,n,m,ans,flag,a[M],b[M],c[N];
bool cover[N<<2];//区间是否被完全覆盖

void init() {
	ans = 0; memset(cover,0,sizeof(cover));
}

void A(int u,int l,int r,int L,int R) {
	if(cover[u]) return;
	if(L <= l && R >= r && !cover[u]) {
		cover[u] = 1; flag = 1; return;
	}
	int mid = (l+r)>>1;
	if(L <= mid) A(u<<1,l,mid,L,R);
	if(R > mid) A(u<<1|1,mid+1,r,L,R);
	cover[u] = cover[u<<1]&cover[u<<1|1];
}

int main() {
	in(T);
	while(T--) {
		int i; in(m);
		init();
		int cnt = 0;
		c[++cnt] = 0; c[++cnt] = 1e9+7;
		for(i = 1;i <= m; ++i) {
			in(a[i]),in(b[i]);
			c[++cnt] = a[i]; c[++cnt] = a[i]+1;
			c[++cnt] = b[i]; c[++cnt] = b[i]+1;
		}
		sort(c+1,c+cnt+1);
		cnt = unique(c+1,c+cnt+1) - c;
		for(i = 1;i <= m; ++i) {
			a[i] = lower_bound(c+1,c+cnt+1,a[i]) - c;
			b[i] = lower_bound(c+1,c+cnt+1,b[i]) - c;
		}
		
		for(i = m;i >= 1; --i) {
			flag = 0;
			A(1,1,cnt,a[i],b[i]);
			if(flag) ++ans;
		}
		out(ans); putchar('\n');
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AndrewMe8211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值