源每一21 线段树
题目链接:http://poj.org/problem?id=2528
题意:就是糊墙给你一面墙让你往上粘海报 粘到最后问你能看见几张
题解:知道了线段树这个玩意之后,看到这个题,就知道他可以用线段树来做,但是它这个点的分布范围太大了,而且线段树一般都是开四倍,内存超妥妥的,这应该怎么办呢?题目给出的样例只有一万组,这样的话可以用离散化的方法来搞这个题。啥叫离散化?就是把一段大区间存储的信息用一段小区间来表示。那这个题来说,当前数组下标代表的是真实坐标的一个映射,也就是这个坐标在这一堆坐标里排什么位置。具体处理方法看代码
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <set> using namespace std; #define lson l,mid,fa<<1 #define rson mid+1,r,fa<<1|1 int a[100000]; int t[100000]; int ans[100000]; int laz[100000]; int coor[100000]; int f[2],num,st,ed,sum; int se[100000]; void Add(int l,int r, int fa) { if(laz[fa]) { laz[fa<<1] = laz[fa]; laz[fa<<1|1] = laz[fa]; ans[fa] = laz[fa]*(r-l+1); laz[fa] = 0; } } void Update(int l,int r,int fa) { if(st<=l&&r<=ed) {laz[fa] = num; return ;} if(ed<l||r<st) return; Add(l,r,fa); int mid = (l+r)>>1; Update(lson);Update(rson); ans[fa] = num*(min(ed,r)-max(st,l)+1); } void Query(int l,int r, int fa) { if (l==r) { if(!se[ans[fa]+laz[fa]]){ sum++; se[ans[fa]+laz[fa]] = 1; } return; } Add(l,r,fa); int mid = (l+r)>>1; Query(lson);Query(rson); } int main() { int c; scanf("%d",&c); while (c--) { memset(ans,0,sizeof(ans)); memset(se,0,sizeof(se)); int n; scanf("%d",&n); for (int i = 1; i <= n*2; i++) scanf("%d",&a[i]); for (int i = 1; i <= n*2; i++) t[i] = a[i]; sort(t+1,t+n*2+1); coor[1] = t[1]; int k = 2; for (int i = 2; i <= n*2; i++) if (t[i-1]<t[i]) coor[k++] = t[i]; for (int i = 1; i <= n; i++) { for (int j = -1; j <= 0; j++) { int tt = a[i*2+j]; int l = 1, r = k-1; while(l<=r) { int mid = (l+r)>>1; if(coor[mid] == tt){f[1+j] = mid; break;} else if(coor[mid]<tt) l = mid+1; else r = mid -1; } } num = i;st = f[0];ed = f[1]; Update(1,k-1,1); } sum = 0; Query(1,k-1,1); printf("%d\n",sum); } return 0; }