Project Euler 485

Project Euler 485

题目

Maximum number of divisors

Let $d(n)$ be the number of divisors of $n$.

Let $M(n,k)$ be the maximum value of $d(j)$ for $n \le j \le n+k-1$.

Let $S(u,k)$ be the sum of $M(n,k)$ for $1 \le n \le u-k+1$.

You are given that $S(1000,10)=17176$.

Find $S(100 000 000,100 000)$.

单调队列

单调队列是一种数据结构,通常用于维护区间的最值。

通常解决的问题是,给定一系列的区间$[l_1,r_1],[l_2,r_2],[l_3,r_3],\dots$的询问,分别求这些区间的最值。而且,$l_1\le l_2\le l_3\le \dots,r_1\le r_2\le r_3\le \dots$,即询问的$l_i,r_i$是不递减的。

那么考虑维护一个队列。一开始,询问的元素都被推进队列中,随着整个程序的运行,序列前面的元素已经过时,不再被询问,被弹出队列;而后面需要被询问的元素逐渐被推入队列中。但是,推入的新元素可能比旧元素更优,那么此时旧元素原地退出队列,让位给新元素,整个过程将会保持队列的单调性。队头的元素最优,队尾的元素最劣。

解决方案

那么,询问值将是$[1,K],[2,K+1],[3,K+2],\dots$因此,使用单调队列来维护这些询问值。最终将每次维护结果的队头相加即可。

代码

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
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=100000000,K=100000;
int f[N+4],v[N+4],pr[N/10+100],m=0;
int main(){
f[1]=1;
for(int i=2;i<=N;i++){
if(v[i]==0) pr[++m]=i,v[i]=i,f[i]=2;
for(int j=1;j<=m;j++){
if(pr[j]>v[i]||pr[j]>N/i) break;
v[i*pr[j]]=pr[j];
f[i*pr[j]]=(v[i]==pr[j]?f[i]*2-f[i/pr[j]]:f[i]*2);
}
}
ll ans=0;
deque<int>q;
for(int i=K,j=1;i<=N;i++){
while(!q.empty()&&i-q.front()>=K) q.pop_front();
for(;j<=i;j++){
while(!q.empty()&&f[j]>=f[q.back()]) q.pop_back();
q.push_back(j);
}
ans+=f[q.front()];
}
printf("%lld\n",ans);
}
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
Ujimatsu Chiya 微信 微信
Ujimatsu Chiya 支付宝 支付宝