第一次写这么长的代码...
题意:
有一长为107的区间,给n个区间染色,问最后可以看到几种颜色。(1≤n≤104)
思路:
区间问题比较适合用线段树处理,因为区间较大,需要将端点离散化集中起来减小内存开支。
Tips:
cstdio是79MS,iostream是625MS。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int M=22000; int tree[M<<4];//完全二叉树存储线段树 int lf[M],rt[M];//存储输入区间左右端点 int ls[3*M],ans;//存储离散化后的新端点 bool vis[3*M];//该颜色是否已计入ans void pushdown(int p) { tree[p<<1]=tree[(p<<1)|1]=tree[p]; tree[p]=-1; } void update(int p,int l,int r,int x,int y,int v)//给p点l~r区间内的x~y区间染上v色 { if(x<=l&&y>=r)//如果该区间被x~y完全覆盖,染上v色 { tree[p]=v; return; } if(tree[p]!=-1) pushdown(p);//若原区间不能被x~y完全覆盖且已染色,将原区间颜色推至子区间,原区间颜色置空 int mid=(l+r)>>1; if(y<=mid) update(p<<1,l,mid,x,y,v);//两区间关系分情况讨论,继续为子区间染色 else if(x>mid) update((p<<1)|1,mid+1,r,x,y,v); else update(p<<1,l,mid,x,mid,v),update((p<<1)|1,mid+1,r,mid+1,y,v); } void query(int p,int l,int r)//从p点开始下查l~r区间有多少种颜色 { if(tree[p]!=-1)//如果该区间已被染色 { if(!vis[tree[p]])//如果该颜色没有计入答案 { ++ans; vis[tree[p]]=true; } return; } if(l==r) return;//如果已访问到底部端点 int mid=(l+r)>>1; query(p<<1,l,mid);//如果该区间染色状况不明,继续向下访问子区间 query((p<<1)|1,mid+1,r); } int main() { int t;scanf("%d",&t); while(t--) { int n;scanf("%d",&n); memset(tree,-1,sizeof(tree));//标记为未染色 memset(vis,false,sizeof(vis)); int len=0; for(int i=0;i<n;i++)//存储输入区间左右端点 { scanf("%d%d",&lf[i],&rt[i]); ls[len++]=lf[i]; ls[len++]=rt[i]; } sort(ls,ls+len); int len2=unique(ls,ls+len)-ls;//离散化,排序 int t=len2; for(int i=1;i<t;i++) { if(ls[i]-ls[i-1]>1)//如果有区间长度大于2,插入一个端点,防止类似于(1,10)(1,4)(4,6)的情况 ls[len2++]=ls[i-1]+1; } sort(ls,ls+len2); for(int i=0;i<n;i++)//x,y为离散化后原端点的映射端点 { int x=lower_bound(ls,ls+len2,lf[i])-ls+1; int y=lower_bound(ls,ls+len2,rt[i])-ls+1; update(1,1,len2,x,y,i); } ans=0; query(1,1,len2); printf("%d\n",ans); } return 0; }