1. 程式人生 > >little w and Segment Coverage 差分割槽間+字首和優化

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;
}