题目链接:http://poj.org/problem?id=2528
这个题目是很经典的离散化成段更新线段树问题,可以试着多做几次,充分理解;
转自:http://www.notonlysuccess.com/index.php/segment-tree-complete/
题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报
思路:这题数据范围很大,直接搞超时+超内存,需要离散化:
离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,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
好吧,现在这个代码只能过poj的数据,并不是真正的完美。。。不知为何一直RE,欢迎指点;
#include<iostream> #include<string> #include<cstdio> #include<cstring> #include<queue> #include<map> #include<cmath> #include<stack> #include<set> #include<vector> #include<algorithm> #define LL long long using namespace std; // 照着题目数组开10005,给报RE,坑啊; int pos[100005][2]; // 用于离散化,存离散化后区间的左右节点; bool vis[100005]; int ans,mm; // ans用于统计最后结构,mm表示第几张海报; struct node1 // 用于离散化的结构体; { int p,n; // p存节点,n存第几张海报; }Line[100005<<1]; struct node2 // 线段树结构体; { int l,r,c; }node[100005<<2]; bool cmp(node1 x,node1 y) // 按左节点递增排序; { return x.p<y.p; } void build(int l,int r,int rt) // 建树; { node[rt].l=l; node[rt].r=r; node[rt].c=0; // 染色标记; if(l!=r){ int mid=(l+r)>>1; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); } } void Insert(int l,int r,int c,int rt) // 更新; { if(node[rt].l==l&&node[rt].r==r){ node[rt].c=c; return; } if(node[rt].c>0&&node[rt].c!=c){ // 说明得更新下一个节点,相当于我之前写的PushDown; node[rt<<1].c=node[rt].c; node[rt<<1|1].c=node[rt].c; node[rt].c=0; } int mid=(node[rt].l+node[rt].r)>>1; if(r<=mid) Insert(l,r,c,rt<<1); else if(l>mid) Insert(l,r,c,rt<<1|1); else{ Insert(l,mid,c,rt<<1); Insert(mid+1,r,c,rt<<1|1); } } void query(int rt) // 区间查询; { if(node[rt].c!=0){ if(!vis[node[rt].c]){ // 统计被染了多少种不同的颜色; ans++; vis[node[rt].c]=true; } return; } query(rt<<1); query(rt<<1|1); } int main() { int t,n,a,b; scanf("%d",&t); while(t--){ scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d%d",&a,&b); // 将a,b区间的长度离散成更小的区间 Line[i<<1].p=a; Line[i<<1|1].p=b; Line[i<<1].n=-(i+1); // 用负数表示左节点; Line[i<<1|1].n=i+1; // 用正数表示右节点; } sort(Line,Line+2*n,cmp); // 排序; int temp=Line[0].p; mm=1; for(int i=0;i<2*n;i++){ if(Line[i].p!=temp){ mm++; temp=Line[i].p; } //if(i>0&&Line[i].p>(Line[i-1].p+1)) mm++; 这条语句其实是我想解决离散化bug的,可是结果却一直RE,导致只过了POJ的较弱数据; if(Line[i].n<0) pos[-Line[i].n-1][0]=mm; else pos[Line[i].n-1][1]=mm; } build(1,mm,1); for(int i=0;i<n;i++){ Insert(pos[i][0],pos[i][1],i+1,1); } memset(vis,false,sizeof(vis)); ans=0; query(1); printf("%d\n",ans); } return 0; }