题意:给你若干个不同色的矩形。问依次覆盖,最后留在平面上的有多少种颜色(要加上空白)?
n<=100000.
标程:
1 #include<bits/stdc++.h> 2 #define fir first 3 #define sec second 4 #define P pair<int,int> 5 using namespace std; 6 int read() 7 { 8 int x=0,f=1;char ch=getchar(); 9 while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} 10 while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 11 return x*f; 12 } 13 const int N=200005; 14 set<int> s[N<<2]; 15 vector<P> vec[N]; 16 int Max[N<<2],Min[N<<2],vis[N],a[N],b[N],c[N],d[N],px[N],py[N],ans,tmp,n; 17 void up(int k,int l,int r) 18 { 19 if (l==r) Max[k]=Min[k]=0; 20 else Max[k]=max(Max[k<<1],Max[k<<1|1]),Min[k]=min(Min[k<<1],Min[k<<1|1]);//统计子节点 21 if ((int)s[k].size())//统计这个节点的覆盖 22 { 23 if (vis[tmp=*s[k].rbegin()]) Min[k]=max(Min[k],tmp); 24 else Max[k]=max(Max[k],tmp); 25 } 26 if (Max[k]<Min[k]) Max[k]=0; 27 } 28 void ins(int k,int l,int r,int L,int R,int x) 29 { 30 if (L<=l&&r<=R) 31 { 32 if (x) 33 if (x>0) s[k].insert(x);else s[k].erase(-x); 34 up(k,l,r); 35 return; 36 } 37 int mid=(l+r)>>1; 38 if (L<=mid) ins(k<<1,l,mid,L,R,x); 39 if (R>mid) ins(k<<1|1,mid+1,r,L,R,x); 40 up(k,l,r); 41 } 42 int main() 43 { 44 n=read(); 45 for (int i=1;i<=n;i++) a[i]=read(),b[i]=read(),c[i]=read(),d[i]=read(),px[++*px]=a[i],px[++*px]=c[i],py[++*py]=b[i],py[++*py]=d[i]; 46 sort(px+1,px+*px+1);sort(py+1,py+*py+1); 47 *px=unique(px+1,px+*px+1)-px-1; 48 *py=unique(py+1,py+*py+1)-py-1; 49 for (int i=1;i<=n;i++) 50 { 51 a[i]=lower_bound(px+1,px+*px+1,a[i])-px; 52 c[i]=lower_bound(px+1,px+*px+1,c[i])-px; 53 b[i]=lower_bound(py+1,py+*py+1,b[i])-py; 54 d[i]=lower_bound(py+1,py+*py+1,d[i])-py-1; 55 vec[a[i]].push_back(P(i,i)); 56 vec[c[i]].push_back(P(i,-i)); 57 } 58 for (int i=1;i<=*px;i++) 59 { 60 for (int j=0;j<vec[i].size();j++) ins(1,1,*py,b[vec[i][j].fir],d[vec[i][j].fir],vec[i][j].sec); 61 while (Max[1]) 62 { 63 vis[Max[1]]=1;ans++; 64 ins(1,1,*py,b[Max[1]],d[Max[1]],0); 65 } 66 } 67 printf("%d ",ans+1); 68 return 0; 69 }
易错点:1.对端点离散化的时候注意直接离散,不要先把d[i]-1。离散化寻址的时候再找前一个。(不然会错)
即是把边染色转换成点染色。
题解:线段树+set+技巧神题
很容易想到扫描线,问题是:1.如何不重复地统计出现过的颜色,2.如何用线段树快速维护颜色层的覆盖。
问题1用一个vis数组打标记,问题2对于线段树的每一个节点维护一个set,表示完全覆盖这个节点的颜色。
对于每一个节点所表示区间露出来的颜色,用Min表示统计过的最小覆盖颜色。Max表示未统计过最大覆盖颜色。(有性质Min和Max只有其一有值)
每次取Max[1]统计答案,并在线段树中消除该颜色的贡献。