悲剧呀,这道题目搞了那么久,原来是离散化的方法出现问题了,悲剧,实在悲剧
线段树 + 离散化
今天做了这道题目的时候, 也算是明白了 离散化 的基本
意思,因为 题目的 数据范围很大 , 1- 10000000,直接线段树的话, 先不说
内存会不会爆, 这么大的范围估计也是 TLE了.
仔细读题, 可以看到 1<= N <= 10000, 也就是说 最多只有 10000个点, 如果
每个点都不同, 那么最多也只有 20000 个数据, 那么离散后的 范围就相当小;
离散化 的大概思路 : 比如说给你一组 数据 1 4 1000 100000, 如果直接
开线段, 显然是浪费, 那么我们只要 进行 映射 :
1 1
4 2
1000 3
100000 4
接下来 我们只要对 1 2 3 4 建立线段树就行了 只需要
[1,4]的区间
嘿嘿,这个是刚写的代码,简洁了许多,也快了一点
#include<iostream> #include<map> #include<set> #define MAXN 10010 using namespace std; struct node { int c; }p[MAXN*8]; int left1[MAXN],right1[MAXN],n,m,num; bool col[MAXN]; map<int,int> map1; map<int,int>::iterator it; void bulid(int k,int s,int t) { if(t==s) return ; p[k].c=0; int kl=k<<1,kr=kl+1,mid=(s+t)>>1; bulid(kl,s,mid); bulid(kr,mid+1,t); } void insert(int k,int s,int t,int l,int r,int e) { if(l>t||r<s) return ; if(l<=s&&t<=r) { p[k].c=e; return ; } int kl=k<<1,kr=kl+1,mid=(s+t)>>1; if(p[k].c!=-1) { p[kl].c=p[kr].c=p[k].c; p[k].c=-1; } insert(kl,s,mid,l,r,e); insert(kr,mid+1,t,l,r,e); } void query(int k,int s,int t) { if(p[k].c!=-1) { if(!col[p[k].c]) { num++; col[p[k].c]=true; } return ; } int kl=k<<1,kr=kl+1,mid=(s+t)>>1; query(kl,s,mid); query(kr,mid+1,t); } int main() { int cas; scanf("%d",&cas); while(cas--) { map1.clear(); scanf("%d",&m); for(int i=0;i<m;i++) { scanf("%d %d",&left1[i],&right1[i]); map1[left1[i]]=10; map1[right1[i]]=10; } it=map1.begin(); int i=1; for(;it!=map1.end();i++,it++) it->second=i; n =i; bulid(1,1,n); for(int i=0;i<m;i++) insert(1,1,n,map1[left1[i]],map1[right1[i]],i+1); num=0; memset(col,false,sizeof(col)); col[0]=true; query(1,1,n); printf("%d\n",num); } return 0; }
#include<set> #include<map> #include<stdio.h> #include<stdlib.h> #include<string.h> using namespace std; #define MAX_N 20010 struct In { int left; int right; }str[MAX_N]; struct Node { int l; int r; int col; }p[8*MAX_N]; set<int> my_set; map<int,int> mp; map<int,int>::iterator beg,end1; int cmp( const void *a , const void *b ){ return *(int *)a-*(int *)b; } void bulid(int k,int s,int t) { int kr,kl,mid; p[k].l=s;p[k].r=t;p[k].col=0; if(s==t) return ; mid=(s+t)>>1;kl=k<<1;kr=kl+1; bulid(kl,s,mid); bulid(kr,mid+1,t); } void insert(int k,int s,int t,int v) { int kr,kl,mid; if(s==p[k].l&&t==p[k].r) { p[k].col=v; } else { mid=(p[k].l+p[k].r)>>1;kl=k<<1;kr=kl+1; if(p[k].col!=-1) { p[kl].col=p[kr].col=p[k].col; p[k].col=-1; } if(t<=mid) insert(kl,s,t,v); else if(s>mid) insert(kr,s,t,v); else { insert(kl,s,mid,v); insert(kr,mid+1,t,v); } } } void query(int k,int s,int t) { if(p[k].col!=-1) { my_set.insert(p[k].col);//将颜色插入set中,自动去重 return ; } int mid=(p[k].r+p[k].l)>>1,kl=k<<1,kr=kl+1; if(t<=mid) query(kl,s,t) ; else if(s>mid) query(kr,s,t); else { query(kl,s,mid);query(kr,mid+1,t); } } int main() { int i,j,cas,n,m,num; scanf("%d",&cas); bulid(1,1,MAX_N); while(cas--) { my_set.clear(); mp.clear(); scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d %d",&str[i].left,&str[i].right); mp[str[i].left] = 10; mp[str[i].right] = 10; } beg = mp.begin(), end1 = mp.end(); insert(1,1,2*n,0); //因为map 已经自动排序了,所以直接从 1 --> N 开始标记, 离散化 for ( int i = 1;beg != end1; ++ beg, ++ i ) { beg->second = i; } //因为线段树已经建立好了, 所以没必要每次都重建一次, 只要插入一条 //覆盖所有区间的 底板 就行了 for ( int i = 1; i <= n; ++ i ) { //用离散后的标记 插入 线段 insert ( 1,mp[str[i].left], mp[str[i].right], i ); } query(1,1,2*n); num=my_set.size();//返回集合中元素个数 if(*my_set.begin()==0) num--; printf("%d\n",num); } return 0; }