bzoj2005 能量采集 莫比乌斯或者普通容斥
/** 题目:bzoj2005 能量采集 链接:https://vjudge.net/contest/178455#problem/F 题意:栋栋有一块长方形的地,他在地上种了一种能量植物,这种植物可以采集太阳光的能量。在这些植物采集能量后, 栋栋再使用一个能量汇集机器把这些植物采集到的能量汇集到一起。 栋栋的植物种得非常整齐,一共有n列,每列 有m棵,植物的横竖间距都一样,因此对于每一棵植物,栋栋可以用一个坐标(x, y)来表示,其中x的范围是1至n, 表示是在第x列,y的范围是1至m,表示是在第x列的第y棵。 由于能量汇集机器较大,不便移动,栋栋将它放在了 一个角上,坐标正好是(0, 0)。 能量汇集机器在汇集的过程中有一定的能量损失。如果一棵植物与能量汇集机器 连接而成的线段上有k棵植物,则能量的损失为2k + 1。例如,当能量汇集机器收集坐标为(2, 4)的植物时,由于 连接线段上存在一棵植物(1, 2),会产生3的能量损失。注意,如果一棵植物与能量汇集机器连接的线段上没有植 物,则能量损失为1。现在要计算总的能量损失。 下面给出了一个能量采集的例子,其中n = 5,m = 4,一共有20 棵植物,在每棵植物上标明了能量汇集机器收集它的能量时产生的能量损失。 在这个例子中,总共产生了36的能 量损失。 思路: 等价于求sigma(gcd(x,y)*2-1) (1<=x<=n,1<=y<=m); = sigma(gcd(x,y)*2)-n*m = 2*sigma(gcd(x,y)) - n*m = 2*sigma(k*gcd) - n*m;(k表示gcd出现的次数) 数据范围小的时候: 定义: f(n)表示gcd(x,y)==n的对数。 F(n)表示n|gcd(x,y)的对数。 因为F(n)包含了gcd是n的倍数的值。所以要把他们减掉。 f(n) = F(n)-f(2*n)-f(3*n)-..-f(k*n); (k*n<=min(n,m)) 后面的n,m是数据范围. 逆序处理即可,注意溢出!!! 数据范围大的时候: 定义: f(n)表示gcd(x,y)==n的对数。 F(n)表示n|gcd(x,y)的对数。 枚举gcd==p,1<=p<=min(n,m); f(p) = sigma(mu[d/p]*F(d)) (p|d) F(d) = (n/d)*(m/d); f(p)表示gcd(x,y)==p的对数。x在[1,n],y在[1,m];等价于:求f(1)表示gcd(x,y)==1的对数。x在[1,n/p],y在[1,m/p]; 用除法的取值,预处理莫比乌斯前缀和。 N*sqrt(N)复杂度。 */ #include <cstdio> #include <cstring> #include <algorithm> #include <set> #include <iostream> #include <vector> #include <map> using namespace std; typedef long long LL; #define ms(x,y) memset(x,y,sizeof x) typedef pair<int, int> P; const LL INF = 1e10; const int mod = 1e9 + 7; const int maxn = 1e5 + 100; LL f[maxn], g[maxn]; /* int prime[maxn], tot, not_prime[maxn]; int mu[maxn], sum[maxn]; void init() { mu[1] = 1; tot = 0; for(int i = 2; i < maxn; i++){ if(!not_prime[i]){ prime[++tot] = i; mu[i] = -1; } for(int j = 1; prime[j]*i<maxn; j++){ not_prime[prime[j]*i] = 1; if(i%prime[j]==0){ mu[prime[j]*i] = 0; break; } mu[prime[j]*i] = -mu[i]; } } for(int i = 1; i < maxn; i++) sum[i] = sum[i-1]+mu[i]; } LL solve(int n,int m)///x在[1,n], y在[1,m] gcd(x,y)=1的对数。 { LL ans = 0; int last; for(int i = 1; i <= n; i=last+1){ last = min(n/(n/i),m/(m/i)); ans += (LL)(sum[last]-sum[i-1])*(n/i)*(m/i); } return ans; }*/ int main() { //freopen("in.txt","r",stdin); int T; int n, m; //init(); while(scanf("%d%d",&n,&m)==2) { if(n>m) swap(n,m); LL ans = 0; for(int i = n; i >= 1; i--){ g[i] = (LL)(n/i)*(m/i); for(int j = i*2; j <= n; j+=i){ g[i] -= f[j]; } f[i] = g[i]; ans += i*f[i]; } printf("%lld\n",ans*2-(LL)n*m);///溢出了啊。 } return 0; }
posted on 2017-08-15 10:19 hnust_accqx 阅读(145) 评论(0) 编辑 收藏 举报