题目链接:http://codeforces.com/gym/101981/attachments
题解:
考虑每个质因子对于整体答案的贡献。
拿第二组样例算一算就不难发现:第 p 个位置上的数,其包含的任意一个素因子,它原本应当产生的贡献有 (n−p+1)⋅p(n−p+1)⋅p,
但是考虑到若其前面出现过一样的素数,那么应当减去一些重复计算的区间。假设它前面的和它一样的素数,最后一次出现在 q 位置,那么就应当减去 (n−p+1)⋅q,即 a[p]a[p] 包含的任意一个质因子其产生的贡献为 (n−p+1)⋅p−(n−p+1)⋅q=(n−p+1)⋅(p−q)。
不妨用 pos[i][k]pos[i][k] 来存储每个素因子的 “pp”,pos[i][k−1]pos[i][k−1] 存储每个素因子的 “qq”。换句话说,pos[i][k]pos[i][k] 代表某个素因子 ii 在 a[1∼n]a[1∼n] 中第 kk 次“出现”的位置是 pos[i][k]pos[i][k];特别地,令 pos[i][0]=0pos[i][0]=0。那么对于任意素因子 ii,它对答案的贡献是 (n−pos[i][k]+1)⋅(pos[i][k]−pos[i][k−1])(n−pos[i][k]+1)⋅(pos[i][k]−pos[i][k−1])。
我们可以对 a[1∼n]a[1∼n] 分解质因数,然后更新相应的 pos[i][k]pos[i][k]。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e6+5; int n,t,a[maxn]; vector<int> pos[maxn]; int prime[maxn],check[maxn]; void LinePrime() { t = 0; for(int i=2; i<maxn; i++) { if(check[i]==0) prime[t++] = i; for(int j=0; j<t; j++) { if(i*prime[j]>maxn) break; check[i*prime[j]] = 1; if(i%prime[j]==0) break; } } } void dec(int p) { int num = a[p]; for(int i=0; i<t&&prime[i]*prime[i]<=num; i++) { if(num%prime[i]==0) pos[prime[i]].push_back(p); while(num%prime[i]==0) num /= prime[i]; } if(num>1) pos[num].push_back(p); } void init(){ LinePrime(); for(int i=0; i<t; i++){ pos[prime[i]].push_back(0); } } int main() { ll ans = 0; init(); cin>>n; for(int i=1; i<=n; i++) { cin>>a[i]; dec(i); } for(int i=0;i<t;i++){ for(int j=1;j<pos[prime[i]].size();j++){ ans+=(ll)(n-pos[prime[i]][j]+1)*(pos[prime[i]][j]-pos[prime[i]][j-1]); } } cout<<ans<<endl; return 0; }
然而,我的逻辑是 考虑若其后面出现一样的素数,就减去重复计算的区间。假设它后面的和它一样的素数,最近一次出现在 q 位置,那么就应当减去 (n−q+1)⋅p,
即 a[p]a[p] 包含的任意一个质因子其产生的贡献为 (n−p+1)⋅p−(n−q+1)⋅p=p(q-p),但是答案总是小于正确答案。。。
错误代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e6+5; int n,t,a[maxn]; vector<int> pos[maxn]; int prime[maxn],check[maxn]; void LinePrime() { t = 0; for(int i=2; i<maxn; i++) { if(check[i]==0) prime[t++] = i; for(int j=0; j<t; j++) { if(i*prime[j]>maxn) break; check[i*prime[j]] = 1; if(i%prime[j]==0) break; } } } void dec(int p) { int num = a[p]; for(int i=0; i<t&&prime[i]*prime[i]<=num; i++) { if(num%prime[i]==0) pos[prime[i]].push_back(p); while(num%prime[i]==0) num /= prime[i]; } if(num>1) pos[num].push_back(p); } void init() { LinePrime(); for(int i=0; i<t; i++) { pos[prime[i]].push_back(0); } } int main() { ll ans = 0; init(); cin>>n; for(int i=1; i<=n; i++) { cin>>a[i]; dec(i); } for(int i=0; i<20; i++) { if(pos[prime[i]].size()==2){ ans += (ll)(n-pos[prime[i]][1]+1)*pos[prime[i]][1]; } for(int j=1; j<pos[prime[i]].size()-1; j++) { cout<<"Prime="<<prime[i]<<endl; cout<<"q="<<pos[prime[i]][j]<<endl; cout<<"p="<<pos[prime[i]][j+1]<<endl; cout<<"P-q="<<pos[prime[i]][j+1]-pos[prime[i]][j]<<endl; ans+=(ll)pos[prime[i]][j]*(pos[prime[i]][j+1]-pos[prime[i]][j]); } } cout<<ans<<endl; return 0; }