您的位置:首页 > 其它

【bzoj 3512】DZY loves math IV

2017-04-13 22:19 344 查看
求∑ni=1∑mj=1ϕ(ij),n不大。

n不大摆明了就是暴力对吧。

考虑S(n,m)=∑mi=1ϕ(in),我们先来看一个搞笑做法。。。

首先n内幂指数大于1的质因子可以拎出来到只剩一次,于是我们只考虑μ(n)≠0的n。接着考虑取n的任意一个质因子p,对于p∣i的指标i,我们规定此时p对ϕ(in)的贡献为p,p−1的贡献由i提供,于是也可以提出来。而对于p∤i的指标i,p的贡献即为p−1。综合两个考虑,如果我们直接把前一类的i当做后一类来处理了,那么我们就要补回1的贡献。于是我们有S(n,m)=(p−1)∑i=1mϕ(npi)+∑i=1m[i%p==0]ϕ(imp)=(p−1)S(n/p,m)+S(n,⌊m/p⌋)

递归终止条件是n==1,然后上杜教筛。看起来很靠谱。。?

然而仔细想想这就是个。。。爆搜。。。对吧= =但是又因为n很小所以不同的质因子数也很少。。最多只有6。。。。所以总共可能的状态大概只有2kw个左右。。

于是我就写了这个玩意。。。然后华丽的狗带了。。。冷静了一下发现。。。空间限制只有128MB?那我2kw个数挂链表都存不下啊。。?一番剪枝剪到了1.3kw。。再也卡不动空间了。。当然如果有谁能卡过去那他很niubia。。。于是重新推了推式子看看能不能搞搞别的。。。

下面是方法二。

考虑直接展开ϕ(in)会是个什么东西。

取d=gcd(i,n),因为μ(n)≠0所以也有μ(d)≠0。。我们单独考虑一个d的质因子p,而前面那个条件就允许我们直接令phi中i的p所贡献的乘积中包含了p−1。。因此我们希望n中的p贡献要恰好为p。。。而实际上这也很好处理。。。当phi(x)中包含p时,p的贡献为p−1,否则为1,而又由于他是个积性的玩意所以每个质因子的贡献独立。。。所以如果我们算∑k∣dϕ(n/k)的话p在这里面的贡献就刚好为p了。因此我们得到ϕ(in)=ϕ(i)∑k∣gcd(i,n)ϕ(n/k)。

然后一波代入式子化简就可以得到S(n,m)=∑k∣nϕ(n/k)S(k,m/k),这样复杂度就是对的了。

你问我复杂度?大概是O(n3/2+m2/3)吧。。

代码:

arr mn;
adj vis , phi , pr;
int ptt;

int n , m , N;

void input() {
n = rd() , m = rd();
N = (int) (pow(m , 0.7) + 0.5);
upmax(N , n);
rep (i , 2 , N) {
if (!vis[i]) {
pr[++ ptt] = i;
phi[i] = i - 1;
if (i <= n)
mn[i] = i;
}
for (int j = 1; j <= ptt && i * pr[j] <= N; j ++) {
int t = i * pr[j];
vis[t] = 1;
if (t <= n) mn[t] = min(mn[i] , pr[j]);
if (i % pr[j] == 0) {
phi[t] = pr[j] * phi[i];
break;
}
phi[t] = (pr[j] - 1) * phi[i];
}
}
phi[1] = 1;
rep (i , 2 , N) phi[i] = add(phi[i] , phi[i - 1]);
}

inline int get(int x) {
int t = 1;
while (x > 1) {
int y = mn[x];
x /= y;
t *= y;
while (x % y == 0) x /= y;
}
return t;
}

__gnu_pbds::gp_hash_table<int , int> ms;

int dls(int n) {
if (n <= N)
return phi
;
if (ms.find(n) != ms.end())
return ms
;
int ret = 1ll * n * (n + 1) / 2 % mod;
for (int l = 2 , r = 0; l <= n; l = r + 1) {
r = n / (n / l);
ret = dec(ret , mul(r - l + 1 , dls(n / l)));
}
ms
= ret;
return ret;
}

__gnu_pbds::gp_hash_table<int , int> exi[maxn];

int gao(int n , int m) {
if (n == 1)
return dls(m);

if (!m) return 0;
if (m == 1) return phi
- phi[n - 1];
if (exi
.find(m)!=exi
.end()) return exi
[m];

int t = 0;
for (int i = 1; i * i <= n; i ++) if (n % i == 0) {
int j = n / i;
t = add(t , mul(phi[j] - phi[j - 1] , gao(i , m / i)));
if (i != j) {
t = add(t , mul(phi[i] - phi[i - 1] , gao(j , m / j)));
}
}

exi
[m]=t;

return t;
}

void solve() {
int ans = 0;
rep (i , 1 , n) {
int x = get(i);
int tmp = gao(x , m);
ans = add(ans , mul(i / x , tmp));
}
printf("%d\n" , ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: