题目链接
题意: 给定 n n n个数和 q q q个询问,询问有两种: 1. 1. 1.给定 i i i和 v a l val val,将 a [ i ] a[i] a[i]的值更新为 v a l val val 2. 2. 2.给定 i i i,求 ∑ j = 1 n a [ j ] ( g c d ( i , j ) = = 1 ) \sum_{j=1}^n a[j](gcd(i,j) == 1) ∑j=1na[j](gcd(i,j)==1)
思路:
考虑询问操作的实现: 由艾弗森约定,得: A n s = ∑ j = 1 n a [ j ] ( g c d ( i , j ) = = 1 ) = ∑ j = 1 n a [ j ] ∑ d ∣ g c d ( i , j ) μ [ d ] Ans = \sum_{j=1}^n a[j](gcd(i,j) == 1) = \sum_{j=1}^n a[j] \sum_{d|gcd(i,j)} \mu[d] Ans=j=1∑na[j](gcd(i,j)==1)=j=1∑na[j]d∣gcd(i,j)∑μ[d]
转换枚举变量为 d d d,则 A n s = ∑ d = 1 n μ [ d ] ∗ ∑ j = 1 ⌊ n d ⌋ a [ j ∗ d ] Ans = \sum_{d=1}^n \mu[d] * \sum_{j=1}^{\lfloor\frac{n}{d}\rfloor}a[j*d] Ans=d=1∑nμ[d]∗j=1∑⌊dn⌋a[j∗d] ( d d d为 i i i的约数)
令 s u m [ x ] = ∑ i = 1 ⌊ n x ⌋ a [ i ∗ x ] sum[x] = \sum_{i=1}^{\lfloor\frac{n}{x}\rfloor} a[i*x] sum[x]=i=1∑⌊xn⌋a[i∗x]
则: A n s = ∑ d = 1 n μ [ d ] ∗ s u m [ d ] Ans = \sum_{d=1}^n \mu[d] *sum[d] Ans=d=1∑nμ[d]∗sum[d] ( d d d为 i i i的约数)
故询问的复杂度为: O ( n ) O(\sqrt n) O(n )
而对于更新操作,只需要更新 s u m sum sum中下标为 i i i的约数的元素值,复杂度为: O ( n ) O(\sqrt n) O(n )
另外需要预处理 s u m sum sum和 μ \mu μ,复杂度为 O ( n ) O(n) O(n)
总复杂度: O ( n + q n ) O(n + q\sqrt n) O(n+qn )
代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<vector> using namespace std; typedef long long ll; #define lson rt<<1 #define rson rt<<1|1 const int A = 1e5 + 10; int a[A],sum[A],pri[A],mu[A],tot,n,q; bool vis[A]; void init(){ for(int i=0 ;i<=n ;i++) sum[i] = 0; for(int i=1 ;i<=n ;i++){ for(int j=i ;j<=n ;j+=i){ sum[i] += a[j]; } } tot = 0,mu[1] = 1; for(int i=2 ;i<A ;i++){ if(!vis[i]) pri[++tot] = i,mu[i] = -1; for(int j=1 ;j<=tot && i*pri[j] < A ;j++){ vis[i*pri[j]] = 1; if(i%pri[j] == 0){ mu[i*pri[j]] = 0; break; } mu[i*pri[j]] = -mu[i]; } } } void update(int x,int val){ for(int i=1 ;i*i<=x ;i++){ if(x%i == 0){ sum[i] = sum[i] - a[x] + val; if(i*i != x){ sum[x/i] = sum[x/i] - a[x] + val; } } } a[x] = val; } ll query(int x){ ll ans = 0; for(int i=1 ;i*i<=x ;i++){ if(x%i == 0){ ans += mu[i]*sum[i]; if(i*i != x){ ans += mu[x/i]*sum[x/i]; } } } return ans; } int main(){ scanf("%d%d",&n,&q); for(int i=1 ;i<=n ;i++) scanf("%d",&a[i]); init(); while(q--){ int type;scanf("%d",&type); if(type == 1){ int id,x; scanf("%d%d",&id,&x); update(id,x); } else{ int id; scanf("%d",&id); printf("%lld\n",query(id)); } } return 0; }