Codeforces 1042G Wafu! 题解 [ 绿 ] [ 数学 ] [ 线性 DP ] [ 前缀和 ] [ 暴力枚举 ]

Wafu!:感觉是比较 naive 的题。

首先可以手模一下样例,发现好像把一个数删掉后还要把它前面的所有数再操作一遍,因此操作数是以很快的速度增长的。写了个前缀和优化 DP 可以发现把 \(x\) 以及其衍生的数完全删掉需要花费 \(2^{x-1}\),也就是说完整删除的元素就是 \(1\sim 30\)。这种操作数为 \(\log\) 级别的 trick 应该还是挺常见的。

因此我们可以先暴力枚举被完整删除的元素,先把能完整删除的元素删完。对于剩下不能完整删除的元素,我们直接模拟,先把这个元素 \(x\) 自己删掉,然后对于产生的 \(1\sim x-1\) 的元素逐个考虑,尝试将它删除。如果遇到一个删不了的,就证明后面的也删不了了,因此递归地对剩下的操作考虑,直到所有操作被执行完毕。不难发现,这一部分可以利用上文预处理出的 DP 值求出。于是总体时间复杂度就是 \(O(T\log^2V)\) 的。

关于时间复杂度的证明:首先完整删除的元素是最多删 \(\log V\) 次的;而对于无法完整删除的元素,递归的过程中显然每一轮最多删 \(\log V\) 次,且最多会递归 \(\log V\) 层(因为每一轮操作的一定是一段前缀,假设最后一次操作的元素为 \(i\),那么这一轮前剩余操作数 \(j\) 满足:\(\sum_{k = 1}^i 2^{k - 1} \le j < 2^{i}\)。其中 \(\sum_{k = 1}^i 2^{k - 1} > 2^{i - 1}\),所以每一轮会使得剩余操作数至少减少到原来的一半),因此时间复杂度是 \(O(T\log^2V)\)

注意 DP 数组的值域最多只到了 \(2\times 10^5\),所以对于越界的访问必须特判,否则会 RE。

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
const int N = 200005;
const ll mod = 1e9 + 7;
ll n, k, s[N], f[N], g[N], smf[N], smg[N], ans = 1;
void init()
{
    f[1] = g[1] = smf[1] = smg[1] = 1;
    for(int i = 2; i < N - 1; i++)
    {
        f[i] = min(ll(1e9 + 5), smf[i - 1] + 1);
        g[i] = (smg[i - 1] * i) % mod;
        smf[i] = min(ll(1e9 + 5), smf[i - 1] + f[i]);
        smg[i] = (smg[i - 1] * g[i]) % mod;
    }
}
void work(ll x)
{
    if(x == 0) return;
    for(int i = 1; i <= 32; i++)
    {
        if(x <= 0) break;
        if(x - f[i] >= 0)
        {
            x -= f[i];
            ans = (ans * g[i]) % mod;
        }
        else
        {
            ans = (ans * i) % mod;
            x--;
            break;
        }
    }
    work(x);
}
void solve()
{
    cin >> n >> k;
    for(int i = 1; i <= n; i++) cin >> s[i];
    sort(s + 1, s + n + 1);
    ans = 1;
    for(int i = 1; i <= n; i++)
    {
        if(k <= 0) break;
        if(s[i] < N && k - f[s[i]] >= 0)
        {
            ans = (ans * g[s[i]]) % mod;
            k -= f[s[i]];
        }
        else
        {
            ans = (ans * s[i]) % mod;
            work(k - 1);
            cout << ans << '\n';
            return;
        }
    }
    cout << ans << '\n';
}
int main()
{
    //freopen("sample.in","r",stdin);
    //freopen("sample.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    init();
    int t;
    cin >> t;
    while(t--) solve();
    return 0;
}
posted @ 2025-08-11 15:17  KS_Fszha  阅读(32)  评论(0)    收藏  举报