POJ 2528 Mayor's posters(离散化+区间set线段树)
http://poj.org/problem?id=2528
题意:
那个城市里要竞选市长,然后在一块墙上可以贴海报为自己拉票,每个人可以贴连续的一块区域,后来帖的可以覆盖前面的,问到最后一共可以看到多少张海报。整块墙可以看成一个数轴,每张海报就是数轴上的一个区间。
分析:
首先题目的区间范围高达1000W,如果直接计算可能超内存且超时间。所以需要离散化。
离散化一:比如对于如下区间集合,[1,1000],[500,2000],[1500,2500].那么把所有区间端点1,500,1000,1500,2000,2500离散化后就是1,2,3,4,5,6.离散化后所得区间为:[1,3],[2,5],[4,6].可以知道离散化前可见区间有3个,但是离散化后只有区间[1,3]和区间[4,6]可见.所以离散化一的方式是有问题的(事后分析,其实我们不需要用到空白区域的信息,所以就算用此方式离散化,最终得到的也是正确结果)。
离散化二:对于区间端点的离散化,如果离散化之前相邻的两个数不是类似于a与a+1的差距1关系,那么就自动在后面的这个数的离散化结果上加1.比如:
[1,10],[1,5],[7,10] 离散化后的区间为[1,7][1,3],[5,7]
离散化方式二主要就是让本来不相邻的数继续保持不相邻即可.
总的处理逻辑是:先读入所有区间,然后把区间端点离散化到map中,然后求出所有新的区间(范围变小了),然后再一次set线段树操作,最后求出线段树中一共有多少种不同的值即可.
另外特别需要注意的:
1.本题如果开头少了int li[MAXN],ri[MAXN];这句话就直接从AC变成运行时出错.虽然我代码中根本没有用到这两个数组,逆天!.
2.本题如果用map来离散化数据,然后查找就会超时.我代码中用的方法是先把所有可能的数读入一个数组num中,然后排序,然后用二分查找原来的值x再num中的位置i,那个位置就是i就是x的新值.注意,由于不相邻的数我们重新映射出来的数也要不相邻,所以这里如果num[i]和num[i-1]不相邻,那么我就插入一个num[i]-1到num数组后面去,这样我们生成的新数值也不相邻.
AC代码:79ms
<span style="font-size:18px;">#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define lson i*2,l,m #define rson i*2+1,m+1,r const int MAXN=11111; bool vis[MAXN*3]; int vis_num; int li[MAXN],ri[MAXN];//我后面没有用到这两个变量,但是如果注释掉这句就是RE,不注释就是AC,逆天 int setv[MAXN*16]; int num[MAXN*3]; void PushDown(int i) { if(setv[i]!=-1) { setv[i*2]=setv[i*2+1]=setv[i]; setv[i]=-1; } } void update(int ql,int qr,int v,int i,int l,int r) { if(ql<=l&&r<=qr) { setv[i]=v; return ; } PushDown(i); int m=(l+r)/2; if(ql<=m) update(ql,qr,v,lson); if(m<qr) update(ql,qr,v,rson); } void query(int i,int l,int r) { if(setv[i]!=-1) { if(vis[setv[i]]==false) { vis_num++; } vis[setv[i]]=true; return ; } if(l==r)return ; int m=(l+r)/2; query(lson); query(rson); } struct node { int l,r; }nodes[MAXN]; int bin(int key,int n,int num[]) { int l=0,r=n-1; while(r>=l) { int mid=(r+l)/2; if(num[mid]==key) return mid; if(num[mid]>key) r=mid-1; else if(num[mid]<key) l=mid+1; } return -1; } int main() { int T; scanf("%d",&T); while(T--) { int nn,m; int n; scanf("%d",&n); nn=0; for(int i=0;i<n;i++) { scanf("%d%d",&nodes[i].l,&nodes[i].r); num[nn++]=nodes[i].l; num[nn++]=nodes[i].r; } sort(num,num+nn); m=1; for(int i=1;i<nn;i++)//去重复的值 if(num[i]!=num[i-1])num[m++]=num[i]; for(int i=m-1;i>0;i--)//如果存在比如1 和3 或3和5 这种相邻值,那么就在序列末尾插入2或 4这种中间值 if(num[i]!=num[i-1]+1)num[m++]=num[i-1]+1; sort(num,num+m); memset(setv,-1,sizeof(setv)); for(int i=0;i<n;i++) { nodes[i].l=bin(nodes[i].l,m,num); nodes[i].r=bin(nodes[i].r,m,num); //printf("%d %d\n",nodes[i].l,nodes[i].r); update(nodes[i].l,nodes[i].r,i,1,0,m-1); } vis_num=0; memset(vis,0,sizeof(vis)); query(1,0,m-1); printf("%d\n",vis_num); } return 0; } </span>