牛客練習賽34 little w and Segment Coverage (差分割槽間)
阿新 • • 發佈:2018-12-15
連結: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。示例1
a表示丟失的線段的編號。
b表示丟失了第a條線段後,沒被覆蓋到的點的個數。
輸入
複製5 3 1 3 4 5 3 4
輸出
複製3 0
說明
若丟失第1條線段,1和2沒被線段覆蓋到。示例2
若丟失第2條線段,5沒被線段覆蓋到。
若丟失第3條線段,所有點都被線段覆蓋到了。
輸入
6 2 1 2 4 5
輸出
複製2 4
說明
若丟失第1條線段,1,2,3,6沒被線段覆蓋到。
若丟失第2條線段,3,4,5,6沒被線段覆蓋到。
題目大意:
給你1..n長度的總區間,外加m條線段。問去掉哪條線段總區間未被覆蓋的點最少。
一開始用線段樹的,T。然後看了官方題解。。:
可以線段樹,但是沒必要。因為是先給出線段最後在做詢問,所以可以用差分割槽間修改,最後 來一遍字首和還原。
然後記錄陣列中被線段僅僅覆蓋 1 次的位置,將這些位置的權值標為 1,做一遍字首和。
然後答案就是 sum[r]-sum[l-1]這樣,注意再加上一開始就沒有被線段覆蓋的點就好了。
所以如果是最後再詢問,那麼可能就不太適合線段樹來做了,畢竟差分割槽間是O(n)的,線段樹常數還大。
話說差分割槽間還真是個神奇的操作啊。也就是先儲存每個點比之前那個點多覆蓋的次數。
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <string> #include <cmath> #include <queue> #include <deque> #include <stack> #include <map> #include <set> typedef long long ll; const int mod=1000000007; const int inf=1000000000; const int maxn=100000; const int maxm=1000000; int left[maxn+10],right[maxn+10]; int cov[maxn+10]; int sum[maxn+10]; int main() { int n,m; scanf("%d%d",&n,&m); memset(cov,0,sizeof(cov)); for(int i=1;i<=m;i++) { scanf("%d%d",left+i,right+i); cov[left[i]]++; cov[right[i]+1]--; } for(int i=1;i<=n;i++) cov[i]+=cov[i-1]; int zero=0; for(int i=1;i<=n;i++) { if(cov[i]==0) zero++; } memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++) { if(cov[i]==1) sum[i]=1; } for(int i=1;i<=n;i++) sum[i]+=sum[i-1]; int ans=-1,uncov=inf; for(int i=m;i>=1;i--) { int temp=sum[right[i]]-sum[left[i]-1]+zero; if(temp<uncov) { ans=i; uncov=temp; } } printf("%d %d\n",ans,uncov); return 0; }View Code