题意:给定一堆线段,求最后重叠了k次或以上的线段和点。
先操作,最后一次下推标记,所以尽管是区间修改,非递归写起来还是很简单。
维护两个线段树,一个维护线段的覆盖,一个维护点的覆盖。
对于线段[L,R],点修改的区间是[L,R],
区间修改中,用线段的左端点代表这条线段,所以区间修改的区间是[L,R-1]
在所有操作都结束之后下推标记,然后从左到右扫描线段输出答案即可。
输出答案的时候,只有在没有线段覆盖了k次或以上的时候,才需要考虑是否有点被覆盖了k次或以上。
因为如果线段包含点的话,点就不需要独立输出了。
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> #define maxn 1000007 using namespace std; //离散化部分 int Rank[maxn<<1],Rn; void SetRank(){//排序+去除重复元素 sort(Rank+1,Rank+1+Rn); int I=1; for(int i=2;i<=Rn;++i) if(Rank[i]!=Rank[i-1]) Rank[++I]=Rank[i]; Rn=I; } int GetRank(int x){//得到某个元素离散化后的下标 int L=1,R=Rn,M; while(L^R){ M=(L+R)>>1; if(Rank[M]<x) L=M+1; else R=M; } return L; } //非递归线段树 int N; int Add[maxn<<3];//区间覆盖次数 int P[maxn<<3];//点覆盖次数 void Build(int n){//建树 N=1;while(N < n+2) N <<= 1; memset(Add,0,sizeof(Add)); memset(P,0,sizeof(P)); } void Update(int L,int R){//区间更新 //线段更新 for(int s=N+L-1,t=N+R;s^t^1;s>>=1,t>>=1){ if(~s&1) ++Add[s^1]; if( t&1) ++Add[t^1]; } //点更新 for(int s=N+L-1,t=N+R+1;s^t^1;s>>=1,t>>=1){ if(~s&1) ++P[s^1]; if( t&1) ++P[t^1]; } } void PushDown(){//下推所有标记 for(int i=1;i<N;++i){ Add[i<<1]+=Add[i]; Add[i<<1|1]+=Add[i]; P[i<<1]+=P[i]; P[i<<1|1]+=P[i]; } } int n,k,l[maxn],r[maxn]; int main(void) { while(~scanf("%d%d",&n,&k)){ //输入 for(int i=Rn=0;i<n;++i){ scanf("%d%d",&l[i],&r[i]); Rank[++Rn]=l[i]; Rank[++Rn]=r[i]; } //离散化 SetRank(); //建树 Build(Rn); //更新树 for(int i=0;i<n;++i) Update(GetRank(l[i]),GetRank(r[i])); //下推标记 PushDown(); //计算答案 int On=0,I=0; for(int i=1;i<=Rn;++i){//扫描覆盖情况 if(Add[N+i]>=k){ if(!On){//碰到线段的左端点,记录 l[++I]=Rank[i]; On=1; } } else{ if(On){//碰到线段的右端点,记录 r[I]=Rank[i]; On=0; } else{//只有在不被线段覆盖时,才会考虑点是否被覆盖了k次或以上 if(P[N+i]>=k){//遇到被覆盖k次或以上的点,记录 l[++I]=Rank[i]; r[I]=Rank[i]; } } } } //输出结果 printf("%d\n",I); for(int i=1;i<=I;++i){ printf("%d %d\n",l[i],r[i]); } } return 0; }