これは本番中には思いつかないな。
http://codeforces.com/contest/655/problem/F
問題
N要素の数列A[i]が与えられる。
また、ここに計Q要素が1つずつ追加される。
要素が追加されるたび、AからK要素を選んだ時のGCDの総和を求めよ。
解法
Editorialを見て回答。
処理中、GCDがaになる組み合わせがそれぞれg(a)個あったとすると、解はsum(a*g(a))である。
ではこのsum(a*g(a))を以下に高速に求めるか。
Aのうち、aを約数とするものの数がd(a)個あったとする。
すると一見である。
ただ、これは同じ数を多重にカウントしている。
約数に6を持つAの要素は、当然2や3も約数に持つので、そこを多重カウントしないようにしなければならない。
本来、GCDがaとなる組み合わせが1つ増えるならば、解はaだけ加算する。
ただ上記のような約数の多重数え上げを防止するようにしたい。
そこでp[i] := (i以下の整数のうち、p[i]と互いに素でp_i未満の数を除いたもの)とする。
すると求める解はではなくとなる。
あとは要素を追加するたびに、求める値がどの程度変化するかを考えよう。
要素が約数qを持つ場合、約数にqを持つ要素数d(q)は1増えるので、だけ増加する。
この差分を各約数毎に求め総和を取ればよい。
int N,K,Q; int num[1010101]; ll co[1010101]; ll mo=1000000007; ll combi(ll N_, ll C_) { const int NUM_=1000001; static ll fact[NUM_+1],factr[NUM_+1],inv[NUM_+1]; if (fact[0]==0) { inv[1]=fact[0]=factr[0]=1; for (int i=2;i<=NUM_;++i) inv[i] = inv[mo % i] * (mo - mo / i) % mo; for (int i=1;i<=NUM_;++i) fact[i]=fact[i-1]*i%mo, factr[i]=factr[i-1]*inv[i]%mo; } if(C_<0 || C_>N_) return 0; return factr[C_]*fact[N_]%mo*factr[N_-C_]%mo; } template<typename V> vector<V> enumdiv(V n) { vector<V> S; for(V i=1;i*i<=n;i++) if(n%i==0) {S.push_back(i); if(i*i!=n) S.push_back(n/i); } sort(S.begin(),S.end()); return S; } void solve() { int i,j,k,l,r,x,y; string s; for(i=1;i<=1000000;i++) co[i]=i; for(i=1;i<=1000000;i++) { for(j=i*2;j<=1000000;j+=i) co[j]-=co[i]; } cin>>N>>K>>Q; while(N--) { cin>>x; auto v = enumdiv(x); FORR(r,v) num[r]++; } ll cur=0; for(i=1;i<=1000000;i++) (cur += co[i]*combi(num[i],K))%=mo; while(Q--) { cin>>x; auto v = enumdiv(x); FORR(r,v) { cur-=co[r]*combi(num[r],K)%mo; num[r]++; cur+=co[r]*combi(num[r],K)%mo; } cur=(cur%mo+mo)%mo; cout<<cur<<endl; } }
まとめ
コードは意外とあっさり。