【題目鏈接】
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=14608
[解題報告】
題目大意:
給定長度區間(L,R)//L,R<=1e7,
給出N個區間操作,把li-ri區間貼上海報 //N《=1e5
求:有多少張海報沒有被完全覆蓋
這道題目的關鍵在於,直接開1e7的線段樹一定會MLE,但我們觀察到一共只有不超過1e5張海報,如果每張海報我們只取它的兩個端點,只對它的端點進行處理,空間複雜度就會大大降低。
所以如何處理呢?
比如區間[1,4],[5,6]有四個不同端點,說明我們需要把他映射到線段樹上四個不同的位置。
我們從1開始對線段樹的葉子節點進行標記,那麼映射到的相對應的線段樹區間爲:
[1,4]->[1,2]
[5,6]->[3,4]
這時候出現一個問題:
我們先後貼上[1,10],[1,4],[5,10],映射爲:[1,4],[1,2],[3,4]只能看見兩張海報
如果先後貼上[1,10],[1,4],[7,10],映射爲:[1,4],[1,2],[3,4],只能看見兩張海報。
哪裏出了問題?
問題在於:
如果兩條相鄰線段並不相交,離散化之後,它變成了相交!
所以維護這樣的性質:如果兩個頂點並不緊鄰,那麼我們離散化之後,它們仍然不能緊鄰。方法是在兩個頂點之間再插入一個頂點。
到了這裏,大部分問題就解決了。
還需要思考一個問題:如何查找沒有被完全覆蓋的海報的數目。
容易想到,我們更新的時候,給區間(l,r)打上標記i表示當前位置的海報是i,這樣最後統計有多少個不同的i即可。
爲了不重複查詢相同的,沒有被完全覆蓋的i,如: (1,10),( 3,4 ),(7,8)更新之後的標記就是1,2,1,3,1,會出現多個第1張海報的標記,我們需要單獨設置一個hash數組,如果某張海報已經被統計過了,就不再重複統計。
這樣,剩下的就是線段樹模板的內容了。
剩下的細節可以參考代碼。我的線段樹風格參考劉汝佳的《訓練指南》。
【參考代碼】
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+50;
int N,m,cnt;
int l[maxn],r[maxn],vis[maxn],X[maxn];
int cover[maxn*4];
void uniq( int nn )
{
m=0;
X[++m]=X[1];
for( int i=2; i<=nn; i++ )
{
if( X[i]!=X[i-1] )X[++m]=X[i];
}
sort( X+1,X+1+m );
int t=m;
for( int i=1;i<t;i++ )
{
if( X[i]!=X[i+1]-1 )X[++m]=X[i]+1;
}
sort( X+1,X+1+m );
}
void build( int O, int L, int R )
{
if(L==R)cover[O]=-1;
else
{
int mid=(L+R)/2;
build(O*2+1,mid+1,R );
build( O*2,L,mid );
cover[O]=-1;
}
}
void pushdown( int O )
{
if( cover[O]!=-1 )
{
cover[O*2]=cover[O*2+1]=cover[O];
cover[O]=-1;
}
}
void update( int O, int L, int R, int qL, int qR, int c )
{
if( qL<=L && R<=qR )
{
cover[O]=c;
}
else
{
pushdown( O );
int mid=(L+R)/2;
if( qL<=mid )update( O*2,L,mid, qL,qR,c );
if( qR>mid )update( O*2+1, mid+1,R,qL,qR,c );
}
}
void query( int O, int L, int R )
{
if( L==R )
{
if( cover[O]!=-1 && !vis[cover[O]] )
{
vis[cover[O]]=1;
cnt++;
}
}
else
{
pushdown(O);
int mid=(L+R)/2;
query( O*2, L,mid );
query( O*2+1,mid+1,R );
}
}
int main()
{
//freopen("2528","r",stdin);
int T; cin>>T;
while(T--)
{
scanf( "%d",&N );
int nn=0;
for( int i=1; i<=N; i++ )
{
scanf( "%d%d",&l[i],&r[i] );
X[++nn]=l[i]; X[++nn]=r[i];
}
sort( X+1,X+1+nn );
uniq(nn);
build( 1,1,m );
for( int i=1; i<=N; i++ )
{
int left=lower_bound( X+1,X+1+m,l[i] )-X;
int right=lower_bound( X+1,X+1+m,r[i] )-X;
update( 1,1,m,left,right,i );
}
memset( vis,0,sizeof vis );
cnt=0;
query( 1,1,m );
printf( "%d\n",cnt );
}
return 0;
}