Enigmatic Partition
题目描述:
数字
n
n
n的分区是所有数字之和等于
n
n
n的集合。
如果分区
n
=
a
1
+
a
2
+
.
.
.
+
a
m
n = a_1 + a_2 + ... + a_m
n=a1+a2+...+am满足以下特征,则称为神秘分区:
- a i a_i ai是整数, 1 ≤ a i ≤ n 1 \le a_i \le n 1≤ai≤n f o r for for 1 ≤ i ≤ m 1 \leq i \leq m 1≤i≤m,并且
- a i ≤ a i + 1 ≤ a i + 1 a_i \leq a_{i + 1} \leq a_i + 1 ai≤ai+1≤ai+1表示 1 ≤ i ≤ m 1 \leq i \leq m 1≤i≤m,并且
- a m = a 1 + 2 a_m = a_1 + 2 am=a1+2
令 f ( n ) f(n) f(n)为 n n n的神秘分区的数量。 给定 l l l和 r r r,请计算 ∑ i = l r f ( i ) \sum_ {i = l} ^ r f(i) ∑i=lrf(i)
输入描述:
第一行包含测试用例的数量
T
(
1
≤
T
≤
10
,
000
)
T(1 \le T \le 10,000)
T(1≤T≤10,000)。
在下面的每条
T
T
T行中,都有两个整数
l
l
l和
r
(
1
≤
l
≤
r
≤
100
,
000
)
r(1 \le l \le r \le 100,000)
r(1≤l≤r≤100,000)。
输出描述:
对于每个测试用例,输出一行包含 C a s e Case Case # x x x: y y y 的行,其中x是测试用例编号,y是答案。
样例输入:
3
5 7
7 9
1 9
样例输出:
Case #1: 2
Case #2: 7
Case #3: 8
说明:
f(1) = 0.
f(2) = 0.
f(3) = 0.
f(4) = 0.
f(5) = 0.
f(6) = 1: 6 = 1 + 2 + 3.
f(7) = 1: 7 = 1 + 1 + 2 + 3.
f(8) = 2: 8 = 1 + 1+ 1 + 2 + 3, 8 = 1 + 2 + 2 + 3.
f(9) = 4: 9 = 1 + 1 + 1 + 1 + 2 + 3, 9 = 1 + 1 + 2 + 2 + 3, 9 = 1 + 2 + 3 + 3, 9 = 2 + 3 + 4
思路:
考虑差分。
首先,我们观察题目所给的
f
(
n
)
f(n)
f(n)的条件:首项末项相差为二,且相邻数字的差值最多为
1
1
1的不下降序列。
所以对于每一个这样的序列我们都可以分成三段:
- a 1 + a 2 + a 3 = m a_1+a_2+a_3=m a1+a2+a3=m
- a 1 , a 2 , a 3 ≥ 1 a_1,a_2,a_3 \ge 1 a1,a2,a3≥1
根据题目意思,我们可以列出下面的等式:
- a 1 v a l + a 2 ( v a l + 1 ) + a 3 ( v a l + 2 ) a_1val+a_2(val+1)+a_3(val+2) a1val+a2(val+1)+a3(val+2)
- = a 1 v a l + a 2 v a l + a 2 + a 3 v a l + 2 a 3 =a_1val+a_2val+a_2+a_3val+2a_3 =a1val+a2val+a2+a3val+2a3
- = v a l ( a 1 + a 2 + a 3 ) + a 2 + 2 a 3 =val(a_1+a_2+a_3)+a_2+2a_3 =val(a1+a2+a3)+a2+2a3
- = v a l m + a 2 + 2 a 3 =valm+a_2+2a_3 =valm+a2+2a3
- = n =n =n
这里我们可以发现,对于每一个
n
n
n都可以分成
4
4
4个未知数:
v
a
l
,
m
,
a
2
,
a
3
val,m,a_2,a_3
val,m,a2,a3,其中
a
2
,
a
3
≥
1
a_2,a_3 \ge 1
a2,a3≥1
想到这里,我们就要引入解这题的重要方法————差分
差分用来求解区间加减求和的问题。
实现:对于每一个区间,我们在差分数组上对这个区间的第一个位置加上修改值,最后一个位置减去修改值,再对差分数组求前缀和,那么这个得到的前缀和数组就是最后经过修改的数组。
简单来说,设原数组是
a
[
]
a[]
a[],差分数组是
b
[
]
b[]
b[],那么
a
i
=
b
i
−
b
i
−
1
a_i=b_i-b_{i-1}
ai=bi−bi−1
对于差分,还有两个变式:
- 二阶差分
二阶差分其实就是差分里再套一个差分,假设要减去或加上一段有规律的数字,可以对差分数组再进行差分,得到一个二阶差分数组,就可以把有规律的转化成同样的数值。
- 隔项差分
隔项差分就是隔一项差分一次。
介绍完差分,回到本题:
刚刚我们推出了:
v
a
l
m
+
a
2
+
2
a
3
=
n
valm+a_2+2a_3=n
valm+a2+2a3=n
以下我们以val=1,m=6为例列表分析,设标记数组为
f
n
f_n
fn:
n | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
---|---|---|---|---|---|---|---|---|---|---|
a 3 = 4 a_3=4 a3=4 | 123333 | |||||||||
a 3 = 3 a_3=3 a3=3 | 112333 | 122333 | ||||||||
a 3 = 2 a_3=2 a3=2 | 111233 | 112233 | 122233 | |||||||
a 3 = 1 a_3=1 a3=1 | 111123 | 111223 | 112223 | 122223 | ||||||
位置: | val*m+3 | (val+1)m+1 | (val+1)m+2 | (val+2)m | ||||||
f n : f_n: fn: | 1 | 1 | 2 | 2 | 2 | 1 | 1 | 0 | 0 | 0 |
差分 | 1 | 0 | 1 | 0 | 0 | -1 | 0 | -1 | 0 | 0 |
隔项 | 1 | 0 | 0 | 0 | -1 | -1 | 0 | 0 | 0 | 1 |
所以我们只要枚举
v
a
l
val
val和
m
m
m的值,在区间首尾进行标记,最后变回原来的数组就可以了
比赛的时候想了半天没想到点子上呜呜呜呜
A C AC AC C o d e Code Code:
#include<bits/stdc++.h>
#define ll long long
const int MAXN=1e5+10;
ll t,l,r,qian[MAXN<<1],f[MAXN<<2];
int main(){
for(int i=3;i<MAXN;++i)
for(int j=i;j<MAXN;j+=i){
f[j+3]++;
f[j+2*i]++;
f[j+i+1]--;
f[j+i+2]--;
}
for(int i=3;i<MAXN;++i) f[i]+=f[i-2];
for(int i=1;i<MAXN;++i){
f[i]+=f[i-1];
qian[i]=qian[i-1]+f[i];
}
scanf("%lld",&t);
for(int Case=1;Case<=t;Case++){
scanf("%lld%lld",&l,&r);
printf("Case #%d: %lld\n",Case,qian[r]-qian[l-1]);
}
}