1. 程式人生 > >[NOIP模擬][並查集]紙帶

[NOIP模擬][並查集]紙帶

題目描述:
題目大意:有一個無限長的紙帶··上面被劃分為若干個格子··現在進行N次操作,第i次操作在L到R上擦出曾經寫上的數字(如果有),並寫上數字i,詢問最終可以看到多少個數字。N≤106,L,R≤109
樣例輸入:

4
0 5
3 8
5 6
4 7

樣例輸出:

3

題目分析:
首先離散化,但注意離散化時候如果相鄰兩個數的差大於1··需要在中間插入一個在兩數之間的數(因為中間本來有空隙,也許它會被染色,但你直接離散化就把這段弄沒了)。
正解:並查集,我們將詢問倒著來,然後用並查集維護每次染色的最右端的位置,這樣當你下一次操作遇到一個已經染過色的點,就直接跳轉到一個沒有染色的點,具體細節見程式碼。理論時間複雜度O(n)。
附程式碼:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<algorithm>
#include<queue>
using namespace std;

const int N=4e6+100;
int
n,m,tot,cnt,b[N],ans,jump[N],check[N],po,ri[N]; bool flag[N/2]; struct node{ int l,r; }a[N/2]; inline int readint() { char ch;int i=0,f=1; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') {ch=getchar();f=-1;} for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3
)+(i<<1)+ch-'0'; return i*f; } inline int find(int x){ if(check[x]==0) return x; return jump[x]=find(jump[x]); } inline void dis_init()//離散化 { sort(b+1,b+tot+1); tot=unique(b+1,b+tot+1)-b-1; m=tot; for(int i=2;i<=tot;i++)//插入 if(b[i]!=b[i-1]+1) b[++m]=b[i-1]+1; sort(b+1,b+m+1); for(int i=1;i<=n;i++) { a[i].l=lower_bound(b+1,b+m+1,a[i].l)-b; a[i].r=lower_bound(b+1,b+m+1,a[i].r)-b; } } int main() { //freopen("ribbon.in","r",stdin); //freopen("ribbon.out","w",stdout); int l,r,x,y; n=readint(); for(int i=1;i<=n;i++) { l=readint()+1;r=readint(); a[i].l=l;a[i].r=r; b[++tot]=l;b[++tot]=r; } dis_init(); for(int i=n;i>=1;i--) { x=a[i].l;y=a[i].r; po=find(y+1);//右端要一直找到一個沒有染色的點 while(x<=y) { if(check[x]!=0) x=find(x);//染過色,跳轉 else { if(flag[i]==false) ans++,flag[i]=true;//統計有多少種顏色(數字) check[x]=i; jump[x]=po; x++; } } } printf("%d",ans); return 0; }