「HEOI2015」公約數數列 題解
阿新 • • 發佈:2020-07-30
分塊。
首先檢視詢問。我們要求最小的 \(p\),使得 \(\gcd(a_0,a_1,\dots,a_p) \times \operatorname{xor}(a_0,a_1,\dots,a_p)=x\)(其中 \(\operatorname{xor}\) 為異或和)。下面我們從 \(1\) 開始標號。
首先能夠得到 \(\gcd\) 的一些性質。
- \(\gcd(\gcd(a_1,a_2,\dots,a_n),\gcd(b_1,b_2,\dots,b_m))=\gcd(a_1,a_2,\dots,a_n,b_1,b_2,\dots,b_m)\),所以兩個塊之間的 \(\gcd\) 可以合併;
- \(\gcd(a_1,a_2,\dots,a_p) \geq \gcd(a_1,a_2,\dots,a_{p+1})\)
- 對於字首 \(\gcd\),每次發生改變,至少變到原來的 \(\dfrac{1}{2}\),即取值最多有 \(O(\log a_i)\) 個。
好了有了這兩個性質我們就有想法了。維護下面四個東西:
- 塊內 \(\operatorname{xor}\) 和;
- 塊內 \(\gcd\);
- 對於每個位置,求出這個位置作為結尾的字首異或和;
- 每一塊頭尾兩個數的字首 \(\gcd\)。(這個可以直接通過第二個東西求出來)。
對於修改,我們直接修改掉原來的元素,然後因為這個修改不會影響其他塊,所以對於這個塊我們暴力重構,時間複雜度 \(O(\sqrt n)\)
對於查詢,如果說這一塊頭尾兩個數的字首 \(\gcd\) 是相等的,說明這塊內沒有發生變化,所以可以直接在排好序的塊內二分查詢查詢是否有解就行了。
否則,暴力查詢,遍歷塊內是否有解。因為不同的 \(\gcd\) 值最多有 \(O(\log a_i)\) 個,所以時間複雜度 \(O(\sqrt n \log ^2 n)\)。
總時間複雜度 \(O(q\sqrt n \log ^2n)\)。
#include<bits/stdc++.h> using namespace std; typedef long long LL; struct block{ LL val,pos; block(){val=pos=0;} block(LL V,LL P){val=V,pos=P;} bool operator < (block another) const {return (val<another.val) || (val==another.val && pos<another.pos);} }s[100005]; LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);} LL n,a[100005],q,p,l[400],r[400],pos[100005],g[100005],x[100005]; void replace(LL t) { g[l[t]]=x[l[t]]=a[l[t]]; s[l[t]]=block(a[l[t]],l[t]); for(LL i=l[t]+1;i<=r[t];++i) { g[i]=gcd(g[i-1],a[i]); x[i]=x[i-1]^a[i]; s[i]=block(x[i],i); } sort(s+l[t],s+r[t]+1); } LL binarysearch(LL l,LL r,LL val) { LL ans=l; while(l<=r) { LL mid=(l+r)>>1; if(s[mid].val>=val) ans=mid,r=mid-1; else l=mid+1; } return ans; } LL query(LL u) { LL ans=114514; LL gd=a[1],xo=0; for(LL i=1;i<=pos[n] && ans==114514;++i) { if(gcd(gd,g[r[i]])==gd) { if(u%gd==0) { LL p=(u/gd)^xo; LL pos=binarysearch(l[i],r[i],p); if(s[pos].val==p) ans=s[pos].pos; } gd=gcd(gd,g[r[i]]); xo^=x[r[i]]; } else { for(LL j=l[i];j<=r[i];++j) { gd=gcd(gd,a[j]); xo^=a[j]; if(gd*xo==u) { ans=j; break; } } } } return ans; } int main(){ scanf("%lld",&n); p=sqrt(n); for(LL i=1;i<=n;++i) { scanf("%lld",&a[i]); pos[i]=(i-1)/p+1; if(!l[pos[i]]) l[pos[i]]=i; r[pos[i]]=i; } for(LL i=1;i<=pos[n];++i) replace(i); LL q; scanf("%lld",&q); while(q-->0) { char o[8]; scanf("%s",o); if(o[0]=='M') { LL where,val; scanf("%lld %lld",&where,&val); ++where; a[where]=val; replace(pos[where]); } else { LL val; scanf("%lld",&val); LL output=query(val); if(output==114514) puts("no"); else printf("%lld\n",output-1); } } return 0; }