解法:
如上图,可以发现长度为7,最小数为1的序列对答案的贡献是有规律的,这种规律可以由5个区间加表示。
因为是区间加,可以采用差分数组来实现,但是有多个区间加,如果每次枚举的长度和最小数都要O(n)时间来维护,那么结果必然是TLE。因为这几个区间是有规律的,即左端点每次+2,右端点每次+1,可以考虑对差分数组建立差分数组来O(1)实现多个区间加。
可以考虑分别建立add数组和del数组,add数组只考虑区间的左端点,del数组只考虑区间的右端点。最后合并add数组和del数组就是f(i)差分数组了。
假设枚举的区间长度为len,最小数字为base,则最左边区间的左端点left=base*len+3,因此可以对add和del的差分数组(此处是2次差分)进行如下操作,即可维护一系列的区间和操作:
add[left]++;add[left+len-2+len-3+1]--;
del[left+len-2]++;del[left+len-2+len-3+1]--;
//为什么右端点都是left+len-2+len-3+1?因为一次差分右端点要右移1,二次差分就要右移2,对应图上的右端点的就是20
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e5+5;
ll add[maxn*3],del[maxn*3],sum[maxn*3];
int main () {
const ll n=1e5;
//首先得到add的隔2差分序列和del的隔1差分序列
for(ll len=3;len<=n;len++){
for(ll base=1;base*len<=n;base++){
ll left=len*base+3;
add[left]++;add[left+len-2+len-3+1]--;
del[left+len-2]++;del[left+len-2+len-3+1]--;
}
}
//然后得到差分序列(add序列和del序列)
for(ll i=3;i<=n;i++){
add[i]+=add[i-2];
del[i]+=del[i-1];
}
//合并差分序列
for(int i=3;i<=n;i++){
add[i]-=del[i];
}
//最后得到原序列
for(ll i=3;i<=n;i++){
add[i]+=add[i-1];
}
//得到前缀和序列
for(ll i=3;i<=n;i++){
sum[i]=sum[i-1]+add[i];
}
ll T;
scanf("%lld",&T);
for(ll kase=1;kase<=T;kase++){
ll l,r;
scanf("%lld%lld",&l,&r);
printf("Case #%lld: %lld
",kase,sum[r]-sum[l-1]);
}
}
参考博客: https://blog.csdn.net/tianyizhicheng/article/details/107773762