题目链接
https://ac.nowcoder.com/acm/contest/5673/E
题目大意
将数 x 拆分,需要满足拆分的数的最大和最小的差值为2,且相邻两个数的差值不能超过1
定义 f(x) 为满足条件的拆分的方案数 , 求 $\sum ^{R}_{i=L}f\left( i\right) $
解题思路
打个表可以发现以 L 为最小值 , len 为长度的拆分方案数是以一定规律变化的
(从某个数开始每隔两项满足条件的方案数+1,从某个数开始每隔两项满足条件的方案数-1)
我们可以枚举 L 和 len , 然后每次达到某个点的时候开始更新方案数
这个操作可以用差分去维护区间的增值和减值
但每次更新需要从当前项不断向后移动两格直到边界 , 时间复杂度很显然是不行的
那怎么办呢?
当然是差分套差分啦( 也就是二阶差分 )
用第一阶差分维护第二阶差分所要维护的区间的增值和减值就OK了
AC_Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10 , M = 1e6 + 10;
int add[M] , f[N];
void init()
{
int up = N - 10;
for(int l = 1 ; l <= up ; l ++)
for(int len = 3 ; len * l <= up ; len ++)
{
add[l * len + 3] ++ , add[(l + 1) * len + 1] -- ;
add[(l + 1) * len + 2] -- , add[(l + 2) * len - 3 + 3] ++ ;
}
for(int i = 3 ; i <= up ; i ++) add[i] += add[i - 2];
for(int i = 1 ; i <= up ; i ++) add[i] += add[i - 1];
for(int i = 1 ; i <= up ; i ++) f[i] = f[i - 1] + add[i];
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
init();
int T = 1;
cin >> T;
for(int k = 1 ; k <= T ; k ++)
{
int l , r;
cin >> l >> r;
cout << "Case #" << k << ": ";
cout << f[r] - f[l - 1] << '\n';
}
return 0;
}