2020暑期牛客多校训练营第八场(E)Enigmatic Partition(数学,二阶隔项差分)

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 1ain f o r for for 1 ≤ i ≤ m 1 \leq i \leq m 1im,并且
  • a i ≤ a i + 1 ≤ a i + 1 a_i \leq a_{i + 1} \leq a_i + 1 aiai+1ai+1表示 1 ≤ i ≤ m 1 \leq i \leq m 1im,并且
  • 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(1T10,000)
在下面的每条 T T T行中,都有两个整数 l l l r ( 1 ≤ l ≤ r ≤ 100 , 000 ) r(1 \le l \le r \le 100,000) r(1lr100,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 a1a2a31

根据题目意思,我们可以列出下面的等式:

  • 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,a31
想到这里,我们就要引入解这题的重要方法————差分

差分用来求解区间加减求和的问题。
实现:对于每一个区间,我们在差分数组上对这个区间的第一个位置加上修改值,最后一个位置减去修改值,再对差分数组求前缀和,那么这个得到的前缀和数组就是最后经过修改的数组。
简单来说,设原数组是 a [ ] a[] a[],差分数组是 b [ ] b[] b[],那么 a i = b i − b i − 1 a_i=b_i-b_{i-1} ai=bibi1
对于差分,还有两个变式:

  • 二阶差分

二阶差分其实就是差分里再套一个差分,假设要减去或加上一段有规律的数字,可以对差分数组再进行差分,得到一个二阶差分数组,就可以把有规律的转化成同样的数值。

  • 隔项差分

隔项差分就是隔一项差分一次。
介绍完差分,回到本题:
刚刚我们推出了:
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

n9101112131415161718
a 3 = 4 a_3=4 a3=4123333
a 3 = 3 a_3=3 a3=3112333122333
a 3 = 2 a_3=2 a3=2111233112233122233
a 3 = 1 a_3=1 a3=1111123111223112223122223
位置:val*m+3(val+1)m+1(val+1)m+2(val+2)m
f n : f_n: fn:1122211000
差分10100-10-100
隔项1000-1-10001

所以我们只要枚举 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]);
	}
}
©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页