1. 程式人生 > >【雜題】[BZOJ4320]【ShangHai2006】Homework【平衡規劃】【並查集】

【雜題】[BZOJ4320]【ShangHai2006】Homework【平衡規劃】【並查集】

Description

需要支援兩種操作 1:在人物集合 S 中加入一個新的程式設計師,其代號為 X,保證 X 在當前集合中不存在。 2:在當前的人物集合中詢問程式設計師的mod Y 最小的值。 (為什麼統計這個?因為拯救 過世界的人太多了,只能取模) 保證第一次為操作1 N≤100000, 1≤X,Y≤300000

Solution

直接統計,很難辦 可以用平衡規劃的思想搞一波 令M=300000M=\sqrt {300000} 對於所有YMY\leq M的詢問,我們在修改時暴力將這些詢問更新即可,然後直接查詢。

對於所有YMY\geq M的詢問,我們可以離線,將詢問和修改倒過來,用並查集維護右邊的第一個是誰,對於每個詢問直接列舉倍數即可(當然也可以值域分塊,相當於區間查詢最小值)。 複雜度O

(NNα(N))O(N\sqrt N\alpha(N))

Code

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 300005
using namespace
std; int mx,n,ri[N],ask[N][2],ans[N],qs[550]; int fd(int k) { if(ri[k]==k) return k; if(!ri[k]) return 0; return ri[k]=fd(ri[k]); } int main() { cin>>n; mx=0; memset(qs,107,sizeof(qs)); fo(i,1,n) { char ch; int x; scanf("\n%c %d",&ch,&x); ask[i][0]=ch-'A'; ask[i][1]=x; if
(ch=='A') mx=max(mx,x),ri[x]=x; } int n1=sqrt(mx); memset(ans,107,sizeof(ans)); fo(i,1,n) { if(ask[i][0]==0) fo(j,1,n1) qs[j]=min(qs[j],ask[i][1]%j); else if(ask[i][1]<=n1) ans[i]=qs[ask[i][1]]; } fod(i,mx,0) if(!ri[i]) ri[i]=ri[i+1]; fod(i,n,1) { if(!ask[i][0]) ri[ask[i][1]]=fd(ask[i][1]+1); else if(ask[i][1]>n1) { for(int j=0;ask[i][1]*j<=mx;j++) { int p=fd(ask[i][1]*j); if(p) ans[i]=min(ans[i],p%ask[i][1]); } } } fo(i,1,n) if(ask[i][0]==1) printf("%d\n",ans[i]); }