Codeforces 612 D. The Union of k-Segments (非递归线段树+离散化)

来源:互联网 发布:linux 退出 编辑:程序博客网 时间:2024/05/17 07:10

题意:给定一堆线段,求最后重叠了k次或以上的线段和点。

先操作,最后一次下推标记,所以尽管是区间修改,非递归写起来还是很简单。

维护两个线段树,一个维护线段的覆盖,一个维护点的覆盖。

对于线段[L,R],点修改的区间是[L,R],

区间修改中,用线段的左端点代表这条线段,所以区间修改的区间是[L,R-1]


在所有操作都结束之后下推标记,然后从左到右扫描线段输出答案即可。

输出答案的时候,只有在没有线段覆盖了k次或以上的时候,才需要考虑是否有点被覆盖了k次或以上。

因为如果线段包含点的话,点就不需要独立输出了。


#include <iostream>#include <cstdio>#include <cmath>#include <algorithm>#include <cstring>#define maxn 1000007using 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;}




0 0
原创粉丝点击