【BZOJ4028】[HEOI2015]公約數數列(分塊)
阿新 • • 發佈:2019-04-26
etc operator 動態 getchar() urn 步驟 href 枚舉 line
【BZOJ4028】[HEOI2015]公約數數列(分塊)
題面
BZOJ
洛谷
題解
看一道題目就不會做系列
首先\(gcd\)最多只會有\(log\)種取值,所以我們可以暴力枚舉出所有可能的\(gcd\)。
那麽我們現在按照步驟要解決兩個問題。第一個是怎麽動態維護\(gcd\)的取值,第二個是怎麽動態維護異或和。
我們考慮分塊。
只維護塊內的前綴\(gcd\)和前綴異或和,這樣子每次修改只需要暴力重構塊。
每次詢問的時候如果塊內的\(gcd\)不變,那麽二分答案,找找有沒有滿足條件的異或和。
否則直接暴力掃這一塊。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<vector> using namespace std; #define ll long long #define MAX 100100 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } const int blk=400; int L[blk],R[blk]; int g[MAX],s[MAX],a[MAX]; struct Data{int id,v;}b[MAX]; bool operator<(Data a,Data b){if(a.v!=b.v)return a.v<b.v;return a.id<b.id;} void Build(int p) { g[L[p]]=s[L[p]]=a[L[p]]; for(int i=L[p]+1;i<=R[p];++i) { g[i]=__gcd(g[i-1],a[i]); s[i]=s[i-1]^a[i]; } for(int i=L[p];i<=R[p];++i)b[i]=(Data){i,s[i]}; sort(&b[L[p]],&b[R[p]+1]); } int n,bel[MAX]; char ch[10]; int main() { n=read(); for(int i=1;i<=n;++i)bel[i]=(i-1)/blk+1; for(int i=1;i<=n;++i)if(!L[bel[i]])L[bel[i]]=i; for(int i=1;i<=n;++i)R[bel[i]]=i; for(int i=1;i<=n;++i)a[i]=read(); for(int i=1;i<=bel[n];++i)Build(i); int Q=read(); while(Q--) { scanf("%s",ch); if(ch[0]=='M') { int x=read()+1,y=read(); a[x]=y;Build(bel[x]); } else { ll x;scanf("%lld",&x); int D=a[1],ans=-1,X=0; for(int i=1;i<=bel[n]&&ans==-1;++i) if(__gcd(D,g[R[i]])==D) { if(x%D==0) { ll k=(x/D)^X; int l=L[i],r=R[i],ret=l; while(l<=r) { int mid=(l+r)>>1; if(b[mid].v>=k)ret=mid,r=mid-1; else l=mid+1; } if(b[ret].v==k){ans=b[ret].id;break;} } D=__gcd(D,g[R[i]]);X^=s[R[i]]; } else for(int j=L[i];j<=R[i];++j) { D=__gcd(D,a[j]);X^=a[j]; if(1ll*D*X==x){ans=j;break;} } if(!~ans)puts("no"); else printf("%d\n",ans-1); } } return 0; }
【BZOJ4028】[HEOI2015]公約數數列(分塊)