传送门:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3547
题意:老板有n个工人,分别被标记上1~n的编号。发工资的时候员工k号能得到 k^4这么多工资。老板想裁掉一些人:编号k和n互质的所有人。求出裁员之后老板能节省下多少钱?
简单来说就是给出n,求所有小于n且和n互质的数i 的四次方和
n = 4 : sum=1+3*3*3*3=82
n = 5 : sum=1+2*2*2*2+3*3*3*3+4*4*4*4=354
思路:对于给定的n (如20)。
先找出所有质因子 (20 能求得2,和5) 。
求出小于n且不被任何一个质因子整除的数的和
(不被2又不被5整除的是1 ,3,7,9,11,13,17,19)
这里求的时候要用容斥定理才有效率。
(对于20,要先排除能被2整除的数,能被5整除的数,再补回能被10整除的数。)
不如还是拿20来演示一次吧。
先求得1~20的四次方和
x = 14+24+34+ 。。。。。。。204
对于2这个因子有2,4,6,8,10,12,14,16,18,20,根据容斥定理,这些的四次方是需要减去的,可以先提一个2出来,这样会得到1~10,可以利用四次方求和公式,得到
x = x -24 *(14+24+34+。。。。。。。104)
这样可以用四次方求和公式求得右边的部分。大O零的效率。
对于5这个因子有5,10,15,20,根据容斥定理,同样是需要减去的,借用上面的方法
x = x -54 *(14+24+34+44)
注意到 10, 20 两项已经被减了两次,所以要加回一次,这也就是容斥定理的内容了
x = x +104 *(14+24)
而容斥定理的实现可以用DFS。
1 ~ n 四次方求和公式:
S = [ (n * ( n + 1) * ( 2 * n + 1) * ( 3 * n * n + 3 * n – 1) ] / 30
#include<iostream>
#include<cstdio>
#include
<limits.h>
#include<cmath>
#include<cstdlib>
#include<cstring>
#define MOD 1000000007LL
using namespace std;
long long prime[2000];
long long factors[2000];
int mark[2000];
long long pow4(long long n){ //求n的四次方,每次乘法后取模保证不会溢出。
long long x= n * n%MOD;
x =( x * x)% MOD;
return x;
}
long long gcd(long long a, long long b) {
return b == 0 ? a : gcd(b, a % b);
}
long long sum(long long n) { //求1~n四次方和
long long a[] = {n, n + 1, 2 * n + 1, 3 * n * n + 3 * n - 1};
long long b = 30;
long long c = 1;
for (int i = 0; i < 4; ++i) {
long long g = gcd(a[i], b);
a[i] /= g;
b /= g;
c = a[i] % MOD * c % MOD;
}
return c;
}
/*
这求和的代码是我copy来的....先分子分母各种约分再相乘取模。如果直接乘完取模再除30会出错。难道说这样子就是确保取模结果出错了的方法么?
*/
long long dfs(int cur,long long p,long long n){ //容斥定理
if(cur==p){
long long x = 1;
long long f = 1;
for(int i = 0 ;i<cur;i++){ f="f" x="">1){
long long y =( f * sum(n / x) )%MOD;//相乘后各种取模
y = (y*pow4(x))%MOD;
return y;
}
return 0; //x== 1 的时候返回0
}
mark[cur] = 0;
long long sum = dfs(cur+1,p,n);
mark[cur] = 1;
sum += dfs(cur+1,p,n);
return sum %MOD; //相加后也要取模,确保不溢出
}
int main(){
// freopen("in.txt","r",stdin); //开大招的痕迹 (- -)||
//freopen("out.txt","w",stdout);
long long i,p;
long long cas,n;
cin>>cas;
while(cas--){
cin>>n;
long long x = n;
p = 0;
for (i = 2; i * i <= n; ++i) { //找质因子压到factors数组里面
if (n % i == 0) {
factors[p++]=i;
do {
n /= i; //找到之后要把质因子从n中完全去掉
} while (n % i == 0);
}
}
if (n > 1) {
factors[p++]=n; //如果n本身是质数,或者质因子太大,需要将残余质
//子也压进去、
}
n = x;
long long ans =( sum(n) + dfs(0,p,n) )%MOD;
//求出1~n四次方和,然后用容斥定理排除
while(ans<0) //求的和有可能是负数,这时候需要加回MOD;
ans += MOD; //这里while可以写成if的。 只不过保守起见,怕加一次不够
ans = ans % MOD; //这个有木有必要?自己想想吧
cout<<ans<<endl;
}
return 0;
}