1. 程式人生 > >[BZOJ4028][HEOI2015]公約數數列(分塊)

[BZOJ4028][HEOI2015]公約數數列(分塊)

先發掘性質:

1.xor和gcd均滿足交換律與結合率。

2.字首gcd最多隻有O(log)個。

但並沒有什麼資料結構能同時利用這兩個性質,結合Q=10000,考慮分塊。

對每塊記錄這幾個資訊:

1.塊內所有數的gcd與異或和。

2.將塊內所有字首異或和放入一個數組排序。

查詢時從前往後遍歷每個塊:

1.若當前塊不使gcd改變,二分查詢是否存在答案。

2.若改變,暴力查詢答案。

複雜度(設塊大小為S,值域為M):

1.修改複雜度$O(S\log S)$

2.查詢複雜度$O(S\log M+\frac{n}{S}\log S)$。

當$S=\sqrt{n}$時複雜度最優,為$O(Q\sqrt{n}\log n)$。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4 typedef long long ll;
 5 using namespace std;
 6 
 7 const int N=100010,K=410;
 8 char op[10];
 9 int n,B,tot,Q,ans,L[K],R[K];
10 ll x,k,a[N],G[K],X[K];
11 struct P{ ll x; int id; }p[N];
12 bool operator <(const P &a,const P &b){ return (a.x==b.x) ? a.id<b.id : a.x<b.x; } 13 14 ll gcd(ll a,ll b){ return b ? gcd(b,a%b) : a; } 15 16 void build(int x){ 17 G[x]=X[x]=a[L[x]]; p[L[x]]=(P){X[x],L[x]}; 18 rep(j,L[x]+1,R[x]) G[x]=gcd(G[x],a[j]),X[x]^=a[j],p[j]=(P){X[x],j};
19 sort(p+L[x],p+R[x]+1); 20 } 21 22 int find(int l,int r,ll k){ 23 while (l<r){ 24 int mid=(l+r)>>1; 25 if (p[mid].x<k) l=mid+1; else r=mid; 26 } 27 return r; 28 } 29 30 int main(){ 31 freopen("bzoj4028.in","r",stdin); 32 freopen("bzoj4028.out","w",stdout); 33 scanf("%d",&n); B=300; tot=(n-1)/B+1; 34 rep(i,1,n) scanf("%lld",&a[i]); 35 rep(i,1,tot) L[i]=(i-1)*B+1,R[i]=min(n,i*B),build(i); 36 for (scanf("%d",&Q); Q--; ){ 37 scanf("%s",op); 38 if (op[0]=='M') scanf("%lld%lld",&x,&k),x++,a[x]=k,build((x-1)/B+1); 39 else{ 40 scanf("%lld",&x); ll g=a[1],t=0; bool w=0; 41 rep(i,1,tot){ 42 if (gcd(g,G[i])==g){ 43 if (x%g==0){ 44 ll k=x/g^t; int ans=find(L[i],R[i],k); 45 if (p[ans].x==(x/g^t)){ printf("%d\n",p[ans].id-1); w=1; break; } 46 } 47 g=gcd(g,G[i]); t^=X[i]; 48 }else 49 rep(j,L[i],R[i]){ 50 g=gcd(g,a[j]); t^=a[j]; 51 if (g*t==x){ printf("%d\n",j-1); w=1; break; } 52 } 53 if (w) break; 54 } 55 if (!w) puts("no"); 56 } 57 } 58 return 0; 59 }