退役后的分治练习

朋友和我说了一道美团面试题。让我对自己的智商产生了一些信心。

这是一篇不成熟的文章,请告诉我怎么减少摘要的最少字数。

题意

告诉你(n,l,r,k),计算有多少个长度为(n)的数列,满足(l leq a\_i leq r,i in [1,n]),且(k | sum_i a_i)。结果对(1e9+7)取模。

数据范围

[n leq 1e5,k leq 100, l leq r leq 1e9 ]

基本思路

一个很好想到的做法是滚动的二维dp,(dp[i][j])表示前(i)个数字对(k)模数为(j)的方案数。那么转移方程为

[dp[i][j] = sum dp[i-1][l] imes dp[i-1][(j-l) \% k] ]

[dp[i] = dp[i-1] otimes dp[i-1] ]

其中(otimes)表示循环卷积。

优化1

上面算法复杂度为(O(nk^2)),这个数据范围有点卡呢~

注意到上面有个卷积!那不妨这个卷积优化一下。
但是(k)也不大,就算优化成(O(nklog k))好像还是有点费劲。

而且还要带精度的FFT,如果模数是(998244353)我会毫不犹豫NTT。

优化2

只能换做法了?

这个题目其实每个位置不具有什么特殊性质,所以我们可以考虑把(n)拆成两段,后面一端是前面的延伸。这样看似乎有了新的方向!

我们分治整个序列。那么这个式子大概可以表示成这个样子。

[f(n)=f(mid) otimes f(n - mid) ]

这里卷积不做优化了,直接最暴力的上,于是我们得到了下面这个(O(k^2 log_2 n))的做法。

#include <bits/stdc++.h>
using namespace std;
const int N = 105;
typedef long long ll;
const ll mod = 1e9 + 7;

vector<ll> ans;

inline ll calcIn(ll r, int k, int i) {
    if (r < i)
        return 0;
    return (r - i) / k + 1;
}

vector<ll> tmp, one;

vector<ll> bake;
inline void conv(vector<ll>& a, vector<ll>& b, int k) {
    assert((int)a.size() == k);
    assert((int)b.size() == k);
    bake.clear();
    for (int i = 0; i < k; i++)
        bake.push_back(a[i]), a[i] = 0;

    for (int i = 0; i < k; i++)
        for (int j = 0; j < k; j++)
            a[(i + j) % k] = (a[(i + j) % k] + bake[i] * b[j] % mod) % mod;
}

void solve(int n, int k, ll l, ll r) {
    if (n == 1) {
        for (int i = 0; i < k; i++)
            one[i] = ans[i] = calcIn(r, k, i) - calcIn(l - 1, k, i);
        return;
    }
    int mid = n >> 1;
    solve(mid, k, l, r);
    tmp.clear();
    for (vector<ll>::iterator it = ans.begin(); it != ans.end(); it++)
        tmp.push_back(*it);
    assert(mid == n - mid || mid == n - mid - 1);
    if (mid == n - mid - 1)
        conv(tmp, one, k);
    conv(ans, tmp, k);
}

int main() {
    int n, k;
    ll l, r;
    scanf("%d%lld%lld%d", &n, &l, &r, &k);
    ans.clear();
    one.clear();
    ans.resize(k);
    one.resize(k);
    solve(n, k, l, r);
    printf("%lld
", ans[0]);
    return 0;
}

欢迎留言指出问题!

一个人没有梦想,和咸鱼有什么区别!
原文地址:https://www.cnblogs.com/TABball/p/12727011.html