2020/8/9 模拟赛 T3 表格
Description
蒟蒻的YHN在用表格(excel)整理题目。它的任务是把所有的题目按照难度, 涂上不同的颜色。红色代表恶心题,黄色代表中档题,绿色代表YHN出的水题。 终于,这枯燥乏味的工作把YHN逼疯了。它疯狂地选中表格中的一些矩形,并同 时给它们涂上某种颜色……当它恢复理智时,表格页面已经变得混乱不堪。 当YHN不停地按Ctrl+Z时,突然想知道,屏幕上能看到多少种颜色? 方便起见,认为所有矩形的颜色互不相同。且整个表格大小为无限大。空位置 为白色(与所有矩形颜色均不同)
Solution
这毒瘤题目毁我青春
用扫描线从左向右扫,维护每次扫到的地方有哪些颜色是第一次看见,更新标记数组,毒瘤就在这维护上
对于线段树上每个区间维护:区间上能看到的颜色最小值,区间上能看到的且第一次看到的颜色最大值,以及区间上能看见和已被覆盖的完全覆盖整个区间的所有颜色(区间里的所有完全覆盖区间的颜色)
对于区间上能看到的颜色的最小值,可能是两个子区间上能看到的颜色的最小值的最小值,也可能是区间里所有完全覆盖区间的颜色的最大值,在以上两者之间取最大值,因为颜色大的会覆盖颜色小的,假如有大的颜色覆盖整个区间,就相当于其它小的颜色全部被覆盖
对于区间上能看到的且第一次看到的颜色最大值,可能是两个子区间上能看到的且第一次看到的颜色的最大值的最大值,也可能是区间里所有完全覆盖区间的颜色的最大值,在以上两者之间取最大值,取最大值的原因同上,注意当区间里所有完全覆盖区间的颜色的最大值小于两子区间上能看到的颜色的最小值的最小值时,说明区间上所有颜色已经被更新,否则,当区间上能看到的且第一次看到的颜色最大值小于区间里所有完全覆盖区间的颜色的最大值时,区间上能看到的且第一次看到的颜色的最大值改为区间里所有完全覆盖区间的颜色的最大值
每次扫描线移动时将线段树根节点的能看到的且第一次看到的颜色的最大值更新,更新该颜色在线段树上对应的区域的所有标记,直至不能更新,扫描线继续移动
对于每个颜色都只会被更新$2$次,用set维护区间里的所有完全覆盖区间的颜色,时间复杂度$O(nlog_{n}^2)$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(3) #include<algorithm> #include<iostream> #include<cstdio> #include<cmath> #include<set> using namespace std; int n,xx[200005],yy[200005],cnt1,cnt2,tot,ll[200005],rr[200005],las=1,ans,tcnt; bool vst[200005]; struct Node { int l,r,x,tag,id; bool operator < (const Node &z)const { if(x==z.x) { return l<z.l; } return x<z.x; } }node[200005]; struct Tree { int maxx,minn; set<int>s; }tree[800020]; inline int read() { int w=0,f=1; char ch=0; while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { w=(w<<1)+(w<<3)+ch-'0'; ch=getchar(); } return w*f; } void build(int i,int l,int r) { tree[i].maxx=tree[i].minn=-1; if(l==r) { return; } int mid=l+r>>1; build(i<<1,l,mid); build(i<<1|1,mid+1,r); } inline void pushup(int i,int l,int r) { if(l==r) { if(tree[i].s.empty()) { tree[i].maxx=tree[i].minn=-1; return; } tree[i].maxx=tree[i].minn=*tree[i].s.rbegin(); if(vst[tree[i].maxx]) { tree[i].maxx=-1; } } else { tree[i].minn=min(tree[i<<1].minn,tree[i<<1|1].minn); tree[i].maxx=max(tree[i<<1].maxx,tree[i<<1|1].maxx); if(tree[i].s.empty()) { return; } tree[i].minn=max(tree[i].minn,*tree[i].s.rbegin()); if(tree[i].maxx<*tree[i].s.rbegin()) { if(vst[*tree[i].s.rbegin()]) { tree[i].maxx=-1; } else { if(*tree[i].s.rbegin()<tree[i].minn) { tree[i].maxx=-1; } else { tree[i].maxx=*tree[i].s.rbegin(); } } } } } void update(int i,int l,int r,int L,int R,int val,int f) { if(L<=l&&r<=R) { if(f==1) { tree[i].s.insert(val); } else { tree[i].s.erase(val); } pushup(i,l,r); return; } int mid=l+r>>1; if(L<=mid) { update(i<<1,l,mid,L,R,val,f); } if(R>mid) { update(i<<1|1,mid+1,r,L,R,val,f); } pushup(i,l,r); } void Push(int i,int l,int r,int L,int R) { if(L<=l&&r<=R) { pushup(i,l,r); return; } int mid=l+r>>1; if(L<=mid) { Push(i<<1,l,mid,L,R); } if(R>mid) { Push(i<<1|1,mid+1,r,L,R); } pushup(i,l,r); } int main() { n=read(); for(int i=1;i<=n;i++) { int xa=read(),ya=read(),xb=read(),yb=read(); node[++tot]=(Node){ya,yb-1,xa,1,i}; node[++tot]=(Node){ya,yb-1,xb,-1,i}; xx[++cnt1]=xa; xx[++cnt1]=xb; yy[++cnt2]=ya; yy[++cnt2]=yb-1; } sort(xx+1,xx+1+cnt1); sort(yy+1,yy+1+cnt2); cnt1=unique(xx+1,xx+1+cnt1)-xx-1; cnt2=unique(yy+1,yy+1+cnt2)-yy-1; for(int i=1;i<=tot;i++) { node[i].l=lower_bound(yy+1,yy+cnt2+1,node[i].l)-yy; node[i].r=lower_bound(yy+1,yy+1+cnt2,node[i].r)-yy; node[i].x=lower_bound(xx+1,xx+cnt1+1,node[i].x)-xx; } for(int i=1;i<=tot;i++) { ll[node[i].id]=node[i].l; rr[node[i].id]=node[i].r; } sort(node+1,node+tot+1); build(1,1,cnt2); for(int i=1;i<=cnt1;i++) { while(las<=tot&&node[las].x==i) { update(1,1,cnt2,node[las].l,node[las].r,node[las].id,node[las].tag); ++las; } while(tree[1].maxx!=-1) { vst[tree[1].maxx]=true; Push(1,1,cnt2,ll[tree[1].maxx],rr[tree[1].maxx]); } } for(int i=1;i<=n;i++) { if(vst[i]) { ans++; } } printf("%d\n",ans+1); return 0; }