1 条题解

  • 0
    @ 2025-8-24 22:38:18

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar lalaouye
    星河滚烫,你是人间理想。

    搬运于2025-08-24 22:38:18,当前版本为作者最后更新于2025-08-19 17:30:26,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    是个美难题。

    第一个想法是假设 yyzz11 该怎么做。我的想法是拆位去做,若 z=1z=1,则每个数位的贡献是 (1+x2iy)(1+x^{2i}y),但是发现这并没有什么卵用,这道题的关键在于如何处理二三进制之间的关系。不过按位处理是必需的,因为这道题一个数的贡献显然可以按位拆贡献,因为位数和在指数就能直接加了。

    我尝试设 f(i,j,k,l,0/1)f(i,j,k,l,0/1) 表示二进制到 ii 位三进制到 jj 位,且二进制数位和为 kk,三进制数位和为 ll,是否卡上界的方案数,但是发现这根本没有办法转移,因为进制之间的关系太复杂了,我们貌似只能考虑对其中一种数位进行 dp,三进制的位数更少,且转移二进制更为方便,我们考虑选择对三进制进行 dp。

    f(i,j,0/1)f(i,j,0/1) 表示考虑完了位数大于 ii 的三进制位,当前二进制数为 jj,是否卡上界的方案数,最后在 i=1i=-1 时统计答案。

    但是这样跑起来比二进制还慢。但是我们注意到关于这种进制数位的题,随便优化一个个位置就是指数级别的优化。我们发现对于 f(i,j,0/1)f(i,j,0/1),不考虑进位的话只会对后 (i+1)log23\lceil(i+1)\log_23\rceil 个数位产生影响,设其为 ef(i)ef(i),因为之后最终的 jj 只会在 [j,j+3i+1)[j,j+3^{i+1}) 中出现。总之一个思想,根据可转移的范围去优化状态,我们设 f(i,j,0/1,0/1)f(i,j,0/1,0/1) 表示考虑完高于 ii 的进制位,当前值模 ef(i)ef(i) 的值为 jj,是否进位,是否顶上界的答案。

    不太熟这种 trick 的人可能有点难以理解,实际上就是将原本的 f(i,j,0/1)f(i,j,0/1)jmodef(i)j\bmod ef(i) 划分到 ef(i)ef(i) 个等价类里,它们是可以完成转移的。

    转移可以根据我们钦定的进位,和加的值以及后继状态是否钦定进位去算固定的转移系数。相当于是说,首先枚举当前位是几,然后枚举后继是否进位,看加起来后看当前能否进位,也就是加起来的数是否 ef(i)\ge ef(i)。可以的话,转移系数就只跟确定位置 11 的个数有关了。

    但是这样没做完,我们忙活半天这个复杂度还是劣完了。我们不难感受到 ii 越大三进制位的选择越少,ii 越小二进制位的选择越少,而进制为的变化带来的力量是巨大的,所以考虑根号分治,让 iBi\ge B 的时候直接暴搜,i<Bi<B 的时候记忆化,这样复杂度就变成 O(3B+n3B)\mathcal{O}(3^B+\frac{n}{3^B}),我的 BB 取的 1212

    这题还有一些其它做法,总之都是利用了位的贡献可拆然后去搞类似分治的东西,这是一个很好的思想。

    代码:

    #include <bits/stdc++.h>
    #define rep(i, l, r) for (int i (l); i <= (r); ++ i)
    #define rrp(i, l, r) for (int i (r); i >= (l); -- i)
    #define eb emplace_back
    using namespace std;
    #define pii pair <int, int>
    #define inf 1000000000ll
    #define ls (p << 1)
    #define rs (ls | 1)
    #define fi first
    #define se second
    constexpr int N = 1e7 + 5, M = 60 + 5, P = 998244353;
    typedef long long ll;
    typedef unsigned long long ull;
    inline ll rd () {
      ll x = 0, f = 1;
      char ch = getchar ();
      while (! isdigit (ch)) {
        if (ch == '-') f = -1;
        ch = getchar ();
      }
      while (isdigit (ch)) {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar ();
      }
      return x * f;
    }
    int qpow (int x, int y) {
      int ret (1);
      for (; y; y >>= 1, x = x * x % P) if (y & 1) ret = ret * x % P;
      return ret;
    }
    int t[N], * o, * f[13][2];
    ll ef[M], py[M], p3[M], n, x, y, z;
    ll pw[M];
    int m;
    int dfs (int step, ll now, bool go, bool up) {
      if (step == -1) return ! go;
      if (step <= 12 && ! up && f[step][go][now] > -1) return f[step][go][now];
      ll ret (0);
      int lim = up ? (n / p3[step] % 3) : 2;
      ll cur (1);
      rep (i, 0, lim) {
        if (i > 0) cur = cur * pw[step] % P * z % P;
        ll tmp (now + p3[step] * i);
        rep (j, 0, 1) {
          ll val (step ? ef[step - 1] : 1);
          ll nxt (tmp + j * val);
          if ((nxt >= ef[step]) == go) {
            nxt -= ef[step] * go;
            ret += py[__builtin_popcountll (nxt / val)] * cur % P * dfs (step - 1, nxt % val, j, up & (i == lim));
          }
        }
      }
      ret %= P;
      if (step <= 12 && ! up) f[step][go][now] = ret;
      return ret;
    }
    int32_t main () {
      n = rd (), x = rd (), y = rd (), z = rd ();
      p3[0] = 1, py[0] = 1;
      ll c (1);
      while (c <= n) c *= 3, ++ m;
      pw[0] = x;
      rep (i, 1, m) p3[i] = p3[i - 1] * 3, pw[i] = pw[i - 1] * pw[i - 1] % P * pw[i - 1] % P;
      rep (i, 1, M - 1) py[i] = py[i - 1] * y % P;
      o = t;
      rep (i, 0, m) {
        ef[i] = 1ll << (int) ceil (log2 (3) * (i + 1));
        if (i <= 12) {
          f[i][0] = o, o += ef[i], f[i][1] = o, o += ef[i];
          rep (j, 0, ef[i] - 1) f[i][0][j] = f[i][1][j] = -1;
        }
      }
      cout << (dfs (m, 0, 0, 1) - 1 + P) % P;
    }
    
    • 1

    信息

    ID
    7697
    时间
    2000ms
    内存
    512MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者