POJ2528 - Mayor's posters(区间替换&&线段切割)
题目大意
在墙上贴海报,海报可以相互覆盖,问最后能够看到多少种海报
题解
方法一:线段树
嘛,就是区间染色问题,和POJ2777一样,不过颜色的种类比POJ2777多多了,最多有10000种。。。所以不能用位运算计算出颜色的总数量来。我们用另外一种方法,多了一个查询函数,在进行完海报的覆盖之后,对整个线段树进行一次查询,查询未清除的标记(碰到一个为标记的标记就回溯),未标记的种类总数就是最终可以看到的海报种类。WA了好多次。。。离散化的时候对位置进行排序的时候被坑了。。。我的离散化是有缺陷的。。。不过POJ数据太弱。。。居然AC了。。。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<bitset> #define MAXN 10005 #define lson l,m,s<<1 #define rson m+1,r,s<<1|1 using namespace std; int setv[MAXN<<2]; bool hash[MAXN<<2]; int cnt; typedef struct { int p; int id; } NODE; NODE a[MAXN*2]; bool cmp(NODE a,NODE b) { return a.p<b.p; } bool cmp2(NODE a,NODE b) { if(a.id==b.id) return a.p<b.p; return a.id<b.id; } void PushDown(int s) { if(setv[s]) { setv[s<<1]=setv[s<<1|1]=setv[s]; setv[s]=0; } } void update(int ql,int qr,int l,int r,int s,int d ) { if(ql<=l&&r<=qr) { setv[s]=d; return; } PushDown(s); int m=(l+r)>>1; if(ql<=m) update(ql,qr,lson,d); if(qr>m) update(ql,qr,rson,d); } void query(int l,int r,int s) { if(setv[s]) { if(!hash[setv[s]]) { cnt++; hash[setv[s]]=true; } return; } if(l==r) return; int m=(l+r)>>1; query(lson); query(rson); } int main(void) { int n,T; cin>>T; while(T--) { scanf("%d",&n); for(int i=1; i<=n; i++) { scanf("%d%d",&a[2*i-1].p,&a[2*i].p); a[2*i-1].id=a[2*i].id=i; } sort(a+1,a+2*n+1,cmp); int pre=-1; int k=0; for(int i=1; i<=2*n; i++) { if(pre!=a[i].p) { pre=a[i].p; a[i].p=++k; } else a[i].p=k; } sort(a+1,a+2*n+1,cmp2); memset(setv,0,sizeof(setv)); for(int i=1; i<=n; i++) update(a[2*i-1].p,a[i*2].p,1,k,1,i); memset(hash,false,sizeof(hash)); cnt=0; query(1,k,1); printf("%d\n",cnt); // for(int i=1; i<=15; i++) //cout<<setv[i]<<endl; } return 0; }
方法二:线段切割
可以作为线段切割的模板题了。。。USACO3.1.4 Shaping Regions的一维形式,直接看代码吧。。应该很容易懂。。。
#include<iostream> #include<cstdio> #include<cstring> #define MAXN 10005 using namespace std; typedef struct { int l; int r; } NODE; NODE a[MAXN]; bool f[MAXN]; int n,ans; void cut(int l,int r,int col,int step) { while((step<n) &&(r<a[step].l||l>a[step].r)) step++; if(step>=n) { if(!f[col]) { ans++; f[col]=true; } return; } if(l<a[step].l) cut(l,a[step].l-1,col,step+1); if(a[step].r<r) cut(a[step].r+1,r,col,step+1); } int main(void) { int T; scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=0; i<n; i++) scanf("%d%d",&a[i].l,&a[i].r); memset(f,false,sizeof(f)); ans=1; f[n-1]=true; for(int i=n-2; i>=0; i--) cut(a[i].l,a[i].r,i,i+1); printf("%d\n",ans); } return 0; }
方法一用了94ms,方法二用了250ms,不过用线段切割做代码相当的简洁