1. 程式人生 > >[BZOJ5288][Hnoi2018]遊戲(暴力+隨機化)

[BZOJ5288][Hnoi2018]遊戲(暴力+隨機化)

傳送門

首先我們考慮暴力做法。
考慮預處理,一開始把相鄰的門沒鎖的點看作一個塊(那麼塊中互相可以到達),記下左右端點。判斷塊左右的門鎖在不在這個塊裡面,如果有就往左右拓展塊,要不然就退出。
然而如果這樣的話在最壞情況,也就是左右不停的跳的話是O(N2)的。
怎麼辦呢?我們可以發現:按順序列舉點拓展效率是非常低的,因為我們會擴充套件到之前的塊。那麼我們就可以每次隨機一個點拓展,那麼這樣的話平攤複雜度就是O(logN),總複雜度為O(NlogN)

#include<cstdio>
#include<iostream>
#include<cstring> #include<ctime> #include<algorithm> #include<cstdlib> #include<cmath> using namespace std; const int INF=1e9; const int N=1000010; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();} while
(ch>='0' && ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } int l[N],r[N]; int pos[N]; int rnd[N]; void pre(int x) { while(1) { bool bk=false; //左邊的門 if(l[x]<=pos[l[x]-1] && pos[l[x]-1]<=r[x]) { l[x]=min(l[x],l[l[x
]-1]); r[x]=max(r[x],r[l[x]-1]); bk=true; } if(l[x]<=pos[ r[x] ] && pos[ r[x] ]<=r[x]) { l[x]=min(l[x],l[r[x]+1]); r[x]=max(r[x],r[r[x]+1]); bk=true; } if(bk==false) break; } } int main() { // freopen("game.in","r",stdin); // freopen("game.out","w",stdout); int n=read(),m=read(),Q=read(); for(int i=1;i<=m;i++) { int x=read(),y=read(); pos[x]=y; } pos[0]=pos[n+1]=n+1; for(int i=1;i<=n;i++) l[i]=r[i]=i;; for(int i=2;i<=n;i++) if(!pos[i-1]) l[i]=l[i-1]; for(int i=n-1;i>=1;i--) if(!pos[i]) r[i]=r[i+1]; for(int i=1;i<=n;i++) rnd[i]=i; random_shuffle(rnd+1,rnd+n+1); for(int i=1;i<=n;i++) pre(rnd[i]); while(Q--) { int x=read(),y=read(); if(l[x]<=y && y<=r[x]) printf("YES\n"); else printf("NO\n"); } return 0; }