little w and Segment Coverage 差分区间+前缀和优化

链接:https://ac.nowcoder.com/acm/contest/297/C
来源:牛客网
 

题目描述

小w有m条线段,编号为1到m。

用这些线段覆盖数轴上的n个点,编号为1到n。

第i条线段覆盖数轴上的区间是L[i],R[i]。

覆盖的区间可能会有重叠,而且不保证m条线段一定能覆盖所有n个点。

现在小w不小心丢失了一条线段,请问丢失哪条线段,使数轴上没被覆盖到的点的个数尽可能少,请输出丢失的线段的编号和没被覆盖到的点的个数。如果有多条线段符合要求,请输出编号最大线段的编号(编号为1到m)。

输入描述:

第一行包括两个正整数n,m(1≤n,m≤10^5)。
接下来m行,每行包括两个正整数L[i],R[i](1≤L[i]≤R[i]≤n)。

输出描述:

输出一行,包括两个整数a b。
a表示丢失的线段的编号。
b表示丢失了第a条线段后,没被覆盖到的点的个数。

示例1

输入

复制

5 3
1 3
4 5
3 4

输出

复制

3 0

说明

若丢失第1条线段,1和2没被线段覆盖到。
若丢失第2条线段,5没被线段覆盖到。
若丢失第3条线段,所有点都被线段覆盖到了。

示例2

输入

复制

6 2
1 2
4 5

输出

复制

2 4

说明

若丢失第1条线段,1,2,3,6没被线段覆盖到。
若丢失第2条线段,3,4,5,6没被线段覆盖到。

****************************************************

前置技能: 差分区间优化区间操作 可用于树状数组和线段树

https://blog.csdn.net/yyx2000/article/details/65937481(我也属现学的)

差分区间修改操作是O(1)的,加上查询是O(n)的;用于直接修改加查询,不用建树了

当然若是边修改边查询 那还是用线段树吧

**********************************************************

题解 就是首先可以这样想 把那段1~n的数列看成都为0 ,在用差分区间处理就好了,覆盖就标记为1 ,在用得到前缀和数组去保存每个点之前的为被覆盖的点  ,哦对了还有一开始初始化后就没有被覆盖得点也先预处理上

有个问题就是这道题是刚开始多组区间初始化,所以差分区间合在一起写,sum前缀和数组处理的时候只用加上,被标记为1的点

,大于1的点,不用管,因为你删除一组区间后,这个点还在。

#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1000000;
int l[maxn];
int r[maxn];
int a[maxn];//实质是标记数组 
int sum[maxn];
int n,m;
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		 cin>>l[i]>>r[i];//差分区间 可以假设初始每个点都是0 题中给出的每段区间都是把这段区间加1,标记上被覆盖 
		                     //题中给出的每段区间都是把这段区间加1,标记+1为是被覆盖 
		 a[l[i]]+=1;   ///差分思想 通过操作差分区间在O(1)内修改差分区间   在O(n)内修改原数组 
		 a[r[i]+1]-=1;
	}
	for(int i=1;i<=n;i++){//还原回去每个点标记后的覆盖情况1为覆盖了一次 大于1 覆盖了多次 
		a[i]=a[i]+a[i-1];  //多个区间导致多个差分数组合并操作 
	}
	int num=0;//原先就没有被覆盖的点 
	for(int i=1;i<=n;i++){
		if(a[i]==0) 
		  num++;
		else if(a[i]==1)//标记被覆盖1次的点  覆盖多次不用管 不用放在sum和里 
		  sum[i]=1;
	}
//	for(int i=1;i<=n;i++){
//		cout<<sum[i]<<" ";
//	}
//	cout<<endl;
	for(int i=1;i<=n;i++){
		sum[i]+=sum[i-1];//前缀和 
	}
//	cout<<num<<endl; 
//	for(int i=1;i<=n;i++){
//		cout<<a[i]<<" ";
//	}
//	cout<<endl;
//	for(int i=1;i<=n;i++){
//		cout<<sum[i]<<" ";
//	}
//	cout<<endl;
	int ans=inf;
	int ant=0;
	for(int i=m;i>=1;i--){  //逆序是因为若为被覆盖的点相同输出编号的的 
		int k=sum[r[i]]-sum[l[i]-1]+num;
	//	cout<<k<<endl;
		if(k<ans){
			ans=k;
			ant=i;
		}
	}
	cout<<ant<<" "<<ans<<endl;
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/gml1999/article/details/85015059