P1447 [NOI2010]能量采集

斯德哥尔摩

2018-08-02 10:08:22

Personal

[P1447 [NOI2010]能量采集](https://www.luogu.org/problemnew/show/P1447) 其实只要细心一点就会发现,题目要求: $$Ans=\sum_{i=1}^n\sum_{j=1}^mf(gcd(i,j)),f(x)=2x-1$$ 带入:$$Ans=\sum_{i=1}^n\sum_{j=1}^m(gcd(i,j)\times 2-1)$$ 展开:$$Ans=2\times \sum_{i=1}^n\sum_{j=1}^mgcd(i,j)-n\times m$$ 于是就转化成了: $$S=\sum_{i=1}^n\sum_{j=1}^mgcd(i,j)$$ 这玩意显然一发莫比乌斯反演啊: $$S=\sum_{d|n}\sum_{i=1}^n\sum_{j=1}^md[gcd(i,j)==d]$$ 即: $$S=\sum_{d|n}d\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==d]$$ 再把$d$换一下枚举方式: $$S=\sum_{d=1}^nd\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==d]$$ 前一个$\sum_{d=1}^nd$直接循环,后面那一堆$\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==d]$就反演就好了。 莫比乌斯反演不会?看这题:[P3455 [POI2007]ZAP-Queries](https://www.luogu.org/blog/49998/p3455-poi2007zap-queries) 附代码: ```cpp #include<iostream> #include<algorithm> #include<cstdio> #define MAXN 100010 using namespace std; int n,m; int k=0,prime[MAXN],mu[MAXN],sum[MAXN]; bool np[MAXN]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } void make(){ int m=MAXN-10; mu[1]=1; for(int i=2;i<=m;i++){ if(!np[i]){ mu[i]=-1; prime[++k]=i; } for(int j=1;j<=k&&prime[j]*i<=m;j++){ np[prime[j]*i]=true; if(i%prime[j]==0)break; else mu[prime[j]*i]=-mu[i]; } } for(int i=1;i<=m;i++)sum[i]=sum[i-1]+mu[i]; } long long solve(long long n,long long m,long long d){ long long s=0; n/=d;m/=d; if(n>m)swap(n,m); for(int i=1,last=1;i<=n;i=last+1){ last=min(n/(n/i),m/(m/i)); s+=(long long)(sum[last]-sum[i-1])*(n/i)*(m/i); } return s; } void work(){ long long ans=0; for(int i=1;i<=n;i++)ans+=solve(n,m,i)*i; printf("%lld\n",ans*2LL-1LL*n*m); } void init(){ n=read();m=read(); if(n>m)swap(n,m); } int main(){ make(); init(); work(); return 0; } ```