题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5528
题目来源:2015长春区域赛金牌题。
简要题意: f(m)=∑i=0m−1∑j=0m−1[ijmodm≢0]g(n)=∑m∣nf(m)[ ] 内为真是 1 否则为 0
求 g(m)mod264数据范围: 1⩽T⩽20000;1⩽n⩽109
考虑 gcd(m,ij)=m 时条件不成立, gcd(m,i)=d 的数有 φ(md) 个。
此时需要 md∣gcd(m,j)⇒md∣j , j 也就是 md 的倍数,有 d 个。
于是有 f(m)=m2−∑d∣mdφ(md)
有了这个结论之后就很开心了,因为可以发现前面和后边都是积性函数,分开算减一下就行了。
前面那个式子我们可以不考虑了,后面那个式子还是有点复杂,容易被卡掉。
接下来令 f(m)=∑d∣mdφ(md)g(n)=∑m∣nf(m)n=∏i=1kpaii
在做的时候首先我想到了poj 2480,以及神题解,由积性函数性质可得
f(paii)=pai−1i(pi+ai(pi−1))
g(n)=∏i=1kg(paii)=∏i=1k∑j=0aif(pji)
A掉之后去看了下叉姐的题解,被瞬间完爆,叉姐神变换如下。
g(n)=∑m∣n∑d∣mdφ(md)=∑d|nd∑md|ndφ(md)=∑d|nd⋅nd=nd(n)
其中 d(n) 为 n 的约数个数,变换我看不懂,但结论简直太优雅。
最终的原来的 g(n) 公式就是 g(n)=∏i=1k∑j=0aip2ji−n∏i=1k(ai+1)
这题非常卡常数,基本上只够你分解个质因数。
而且还需要打素数表加速分解质因数。
能够分析出来答案的话整个程序还比较好写。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define fi first
#define se second
using namespace std;
typedef unsigned long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
LL powmod(LL a,LL b, LL MOD) {LL res=1;a%=MOD;for(;b;b>>=1){if(b&1)res=res*a%MOD;a=a*a%MOD;}return res;}
// head
bool vis[32000];
int p[10000];
int tot = 0;
void getPrime(int n) {
for (int i = 2; i <= n; i++) {
if (!vis[i]) p[++tot] = i;
for (int j = 1; j<=tot && i*p[j]<=n; j++) {
vis[i*p[j]] = true;
if (i%p[j]==0) break;
}
}
}
vector fac;
LL get(int x) {
LL ans = 1, ans2 = 1;
for (int i = 0; i < fac.size(); i++) {
LL mul = 1, sum2 = 1;
for (int j = 1; j <= fac[i].se; j++) {
mul *= fac[i].fi;
sum2 += mul*mul;
}
ans *= (fac[i].se+1);
ans2 *= sum2;
}
return ans2-ans*x;
}
void getFac(int m) {
for (int i = 1; p[i]*p[i] <= m; i++) {
if (m % p[i] == 0) {
int cnt = 0;
while (m % p[i] == 0) cnt++, m /= p[i];
fac.pb(mp(p[i], cnt));
}
}
if (m > 1) fac.pb(mp(m, 1));
}
int main() {
getPrime(31999);
int t, n;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
getFac(n);
printf("%I64u\n", get(n));
fac.clear();
}
return 0;
}