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>