标签:
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 56864 | Accepted: 16445 |
Description
Input
Output
Sample Input
1 5 1 4 2 6 8 10 3 4 7 10
Sample Output
4
Source
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> using namespace std; #define N 1005 int m,ans,li[N],ri[N]; int x[N<<3],col[N<<4],hash[N]; void pushdown(int rt){ col[rt<<1]=col[rt<<1|1]=col[rt]; col[rt]=-1; } void updata(int rt,int l,int r,int val,int x,int y){ if(x>=l&&y<=r){ col[rt]=val;return ; } if(col[rt]!=-1) pushdown(rt); int mid=(x+y)>>1; if(mid>=l) updata(rt<<1,l,r,val,x,mid); if(mid<r) updata(rt<<1|1,l,r,val,mid+1,y); } void query(int rt,int l,int r){ if(l==r){ if(!hash[col[rt]]){ hash[col[rt]]=1; ans++; } return ; } if(col[rt]!=-1) pushdown(rt); int mid=(l+r)>>1; query(rt<<1,l,mid); query(rt<<1|1,mid+1,r); } int search(int ll,int hh,int xx){ while(ll<=hh){ int mid=(ll+hh)>>1; if(x[mid]==xx) return mid; else if(x[mid]>xx) hh=mid-1; else ll=mid+1; } return -1; } int main(){ int t,n; scanf("%d",&t); while(t--){ memset(col,-1,sizeof col); memset(hash,0,sizeof hash); int tot(0); scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d",&li[i],&ri[i]); x[++tot]=li[i]; x[++tot]=ri[i]; } sort(x+1,x+tot+1); m=1; for(int i=2;i<=tot;i++){ if(x[i]!=x[i-1]) x[++m]=x[i]; } for(int i=m;i>1;i--){ if(x[i]-x[i-1]>1) x[++m]=x[i]-1; } sort(x+1,x+m+1); for(int i=1;i<=n;i++){ int l=search(1,m,li[i]); int r=search(1,m,ri[i]); updata(1,l,r,i,1,m); } ans=0; query(1,1,m); printf("%d\n",ans); } return 0; }
题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报
思路:这题数据范围很大,直接搞超时+超内存:
需要:线段树2的板子+离散化
离散化,如下面的例子(题目的样例),因为单位1是一个单位长度,将下面的
1 2 3 4 6 7 8 10
— — — — — — — —
1 2 3 4 5 6 7 8
离散化 X[1] = 1; X[2] = 2; X[3] = 3; X[4] = 4; X[5] = 6; X[7] = 8; X[8] = 10
于是将一个很大的区间映射到一个较小的区间之中了,然后再对每一张海报依次更新在宽度为1~8的墙上(用线段树),最后统计不同颜色的段数。
但是只是这样简单的离散化是错误的,
如三张海报为:1~10 1~4 6~10
离散化时 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10
第一张海报时:墙的1~4被染为1;
第二张海报时:墙的1~2被染为2,3~4仍为1;
第三张海报时:墙的3~4被染为3,1~2仍为2。
最终,第一张海报就显示被完全覆盖了,于是输出2,但实际上明显不是这样,正确输出为3。
新的离散方法为:在相差大于1的数间加一个数,例如在上面1 4 6 10中间加5(算法中实际上1,4之间,6,10之间都新增了数的)
X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 5, X[ 4 ] = 6, X[ 5 ] = 10
这样之后,第一次是1~5被染成1;第二次1~2被染成2;第三次4~5被染成3
最终,1~2为2,3为1,4~5为3,于是输出正确结果3。
所以离散化要保存所有需要用到的值,排序后,分别映射到1~n,这样复杂度就会小很多很多
而这题的难点在于每个数字其实表示的是一个单位长度(并且一个点),这样普通的离散化会造成许多错误(包括我以前的代码,poj这题数据奇弱)
给出下面两个简单的例子应该能体现普通离散化的缺陷:
1-10 1-4 5-10
1-10 1-4 6-10
为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]
如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.
线段树功能:update:成段替换 query:简单hash
AC代码:
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; #define N 10005 int m,li[N],ri[N]; int x[N<<3],col[N<<4],ans; bool hash[N]; void pushdown(int rt) { col[rt<<1]=col[rt<<1|1]=col[rt]; col[rt]=-1; } void updata(int rt,int L,int R,int c,int l,int r){ if(l>=L&&r<=R){ col[rt]=c;return ; } if(col[rt]!=-1) pushdown(rt); int mid=(l+r)>>1; if(mid>=L) updata(rt<<1,L,R,c,l,mid); if(mid<R) updata(rt<<1|1,L,R,c,mid+1,r); } void query(int rt,int l,int r){ if(l==r){ if(!hash[col[rt]]){ hash[col[rt]]=1; ans++; } return; } if(col[rt]!=-1) pushdown(rt); int mid=(l+r)>>1; query(rt<<1,l,mid); query(rt<<1|1,mid+1,r); } int search(int ll,int hh,int xx){ int mid; while (ll<=hh){ mid=(ll+hh)>>1; if(x[mid]==xx) return mid; else if(x[mid]>xx) hh=mid-1; else ll=mid+1; } return -1; } int main(){ int t,n,i; scanf("%d",&t); while(t--){ memset(col,-1,sizeof(col)); memset(hash,0,sizeof(hash)); int tot=0; scanf("%d",&n); for(i=1;i<=n;i++){ scanf ("%d %d",&li[i],&ri[i]); x[++tot]=li[i]; x[++tot]=ri[i]; } sort(x+1,x+tot+1); m=1; for(i=2;i<=tot;i++){ if(x[i]!=x[i-1]) x[++m]=x[i]; } for(i=m;i>1;i--){ if(x[i]-x[i-1]>1) x[++m]=x[i]-1; } sort(x+1,x+m+1); for(i=1; i<=n; i++){ int l=search(1,m,li[i]); int r=search(1,m,ri[i]); updata(1,l,r,i,1,m); } ans=0; query(1,1,m); printf("%d\n",ans); } return 0; }
标签:
原文地址:http://www.cnblogs.com/shenben/p/5601349.html