Project Euler 378

Project Euler 378

题目

Triangle Triples

Let $T(n)$ be the $n^\text{th}$ triangle number, so $T(n) = \dfrac{n(n + 1)}{2}$.

Let $dT(n)$ be the number of divisors of $T(n)$.

E.g.: $T(7) = 28$ and $dT(7) = 6$.

Let $Tr(n)$ be the number of triples $(i, j, k)$ such that $1 \le i \lt j \lt k \le n$ and $dT(i) \gt dT(j) \gt dT(k)$

$Tr(20) = 14$, $Tr(100) = 5772$, and $Tr(1000) = 11174776$.

Find $Tr(60 000 000)$.

Give the last $18$ digits of your answer.

解决方案

令$N=600000000$,通过线性筛预处理处理出$1\sim N+1$的因数个数,可以以$O(N)$的时间复杂度计算出所有$dT$值,令$M$是$1\sim N$中$dT$值最大的一个。

考虑使用动态规划的思想解决本题。令状态$(i,j)(1\le i\le 3,1\le j\le N)$表示长度为$i$并且以$j$为结尾的序列有多少个(即$p1<p_2<\dots<p{i-1}dT(p2)>\dots>dT(p{i-1})>j$)

那么不难写出关于$f(i,j)$的状态转移方程:

直接进行转移的效率非常低,因此考虑使用树状数组维护状态:将当前所有$f(i-1,k),k\le j$的状态都按照值$dT(k)$存到树状数组中,那么询问时只需要以$O(\log M)$的时间复杂度求得大于$dT(j)$的所有状态之和。

最终答案为$\sum_{j=1}^Nf(3,j)$,时间复杂度为$O(N\log M)$。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <bits/stdc++.h>
# define lb(i) ((i)&(-i))
typedef long long ll;
using namespace std;
const int N=60000000;
ll mod=1e18;
int M=0;
int v[N+4],pr[N/10+100],m=0;
int d[N+4];
ll s[N+4],f[N+4];
void pl(ll &x,ll y){
x+=y;
if(x>=mod) x-=mod;
}
void add(int p,ll x){
for(int i=p;i<=M;i+=lb(i)){
pl(s[i],x);
}
}
ll que(int p){
ll a=0;
for(int i=p;i;i-=lb(i))
pl(a,s[i]);
return a;
}
int main(){
f[1]=1;
for(int i=2;i<=N+1;i++){
if(v[i]==0){
v[i]=i;pr[++m]=i;f[i]=2;
}
for(int j=1;j<=m;j++){
if(pr[j]>v[i]||pr[j]>(N+1)/i) break;
v[i*pr[j]]=pr[j];
f[i*pr[j]]=f[i]*2-(pr[j]==v[i]?f[i/pr[j]]:0);
}
}
for(int n=1;n<=N;n++)
d[n]=n&1?f[n]*f[(n+1)>>1]:f[n>>1]*f[n+1];
M=*max_element(d+1,d+N+1);
for(int i=1;i<=N;i++)
f[i]=1;
for(int k=1;k<=2;k++){
memset(s,0,sizeof(s));
ll sum=0;
for(int i=1;i<=N;i++){
ll x=f[i];
f[i]=sum-que(d[i]);
add(d[i],x);
pl(sum,x);
}
}
ll ans=0;
for(int i=1;i<=N;i++)
pl(ans,f[i]);
printf("%lld\n",ans);
}