[NOIP模擬][並查集]紙帶
阿新 • • 發佈:2019-01-26
題目描述:
題目大意:有一個無限長的紙帶··上面被劃分為若干個格子··現在進行N次操作,第i次操作在L到R上擦出曾經寫上的數字(如果有),並寫上數字i,詢問最終可以看到多少個數字。N≤
樣例輸入:
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;
}