首先可以把問題化為:支援一個資料結構,可以加入和刪除數,並且詢問目前裡面有幾個數和給定的某個數互質。設當前詢問的數是x,其因數由為d_1,...,d_r,那麼x和所有當前裡面的數的 gcd 值只有可能是x的因數,因此設目前總共有a_i的數和x的 gcd 等於i,則只有x的因數的a值非0,而我們想求的就是a_1。單一個a_i的值不容易求,但如果令num_i代表目前這些數裡面有幾個數是i的倍數,顯然可以在加入和刪除一個數時維護好num_i的陣列,那麼考慮num_{d_i},他其實就會等於所有滿足d_i | d_j 的a_{d_j} 的總和,也就是如果令A_i=\{ d_j | d_j\%d _i=0 \},那麼\displaystyle num_{d_i}=\sum_{t\in A_i}a_t,因此現在要用已知的num_{d_i}們的式子來求出a_1。再來就有點類似莫比烏斯反演的概念了,那東西的詳細可以參考維基,其實和這題一樣本質上來說就是排容原理。不妨設d_1,...,d_k就是x的所有質因數(不包含1),那麼考慮集合A_1,...,A_k,跟他們的宇集\{ d_1,...,d_r \},並且以下記|S|代表S中所有元素的a值的和。可以得到A_1\bigcup A_2\bigcup ... \bigcup A_k包含了所有x的非1的因數,因此用宇集扣掉他之後就只剩下1,也就是我們要求扣掉之後的集合內元素的權值和(雖然他只有一個元素)。這時就可以用排容了,首先宇集的權值和就是num_1,再來要扣掉|A_1|,...,|A_k|,由上面的結論可以知道|A_i|就是num_{d_i},直接代入即可。再來要加回長的像|A_i\bigcap A_j|的這種東西,這裡就是莫比烏斯變換關鍵的地方,因為d_i和d_j是兩個x的不同的質因數,所以他們互質,而回到A_i的定義,他裡面蒐集了所有是d_i倍數的x的因數,所以由d_i,d_j互質就可以得到|A_i\bigcap A_j|=num_{i\cdot j},因為他們的交集就代表著滿足他是d_i,d_j倍數的x的因數,所以是滿足他是d_i\cdot d_j倍數的x的因數,所以就可以化簡成num_{i\cdot j}了。到了好幾個A交集在一起時一樣可以融合起來變成一個num值,於是這樣就成功求出答案了。
而實作的方法又轉了個彎,因為注意到上面那條式子裡面,我們會用到的num值的下標都是「沒有平方因子的數」(square-free),或是說把他質因數分解的話會分成好幾個不同的質數的一次方相乘,而num值前面的係數則和當前下標的質因數個數有關,若個數為奇數則為-1,若為偶數則為1,這東西就是莫比烏斯函數,因此在計算上我們只要先把x的所有因數找出來,求出所有因數的num值乘上其莫比烏斯函數的值的總和就可以了。
code :
#include<bits/stdc++.h> #define LL long long using namespace std; const int maxn=500000+10 ; int mu[maxn],di[maxn] ; bool vis[maxn] ; void getmu() { for(int i=2;i*i<maxn;i++) if(!vis[i]) for(int j=i*i;j<maxn;j+=i) vis[j]=1 , di[j]=i ; for(int i=2;i<maxn;i++) { if(!vis[i]){mu[i]=-1 ; continue ;} mu[i]= (i/di[i])%di[i] ? mu[i/di[i]]*-1 : 0 ; } } int a[maxn],num[maxn] ; bool on[maxn] ; vector<int> vd ; void getdiv(int x) { vd.clear() ; for(int i=1;i*i<=x;i++) if(x%i==0) { vd.push_back(i) ; if(i*i!=x) vd.push_back(x/i) ; } } int getval() { int ret=num[1] ; for(auto i : vd) ret+=num[i]*mu[i] ; return ret ; } inline void add(int x) { for(auto i : vd) num[i]+=x ; } main() { getmu() ; int n,Q ; scanf("%d%d",&n,&Q) ; for(int i=1;i<=n;i++) scanf("%d",&a[i]) ; LL ans=0 ; while(Q--) { int x ; scanf("%d",&x) ; getdiv(a[x]) ; if(!on[x]) ans+=getval() , add(1) , on[x]=1 ; else add(-1) , ans-=getval() , on[x]=0 ; printf("%I64d\n",ans) ; } }
沒有留言:
張貼留言