2020牛客暑期多校训练营(第八场)Enigmatic Partition

题目链接

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;
}
posted @ 2020-08-05 02:55  GsjzTle  阅读(193)  评论(0编辑  收藏  举报