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;
}