Jumping steps 题解

https://www.luogu.com.cn/problem/U230162

中文题意

共有 \(n\) 级台阶,编号分别为 \(1\sim n\),令初始位置为 \(0\),现在想要跳到 \(n\)。其中有 \(m\) 个障碍物,\(0,n\) 一定不是障碍物,任意时刻不能位于障碍物位置。每次可以从位置 \(x\) 跳到 \(x+k(k>0)\),若跨过超过 \(S\) 个障碍物得 \(0\) 分,否则得 \(k^2\) 分。最终分数为每一步分数之和。问所有从 \(0\) 跳到 \(n\) 的不同方案的分数之和。

\(n\leq 10^9,m\leq2\times10^6\)

题解(怎么这么啰嗦?)

注意到一个跳跃方案中的若干次跳跃的贡献是相加的,于是可以考虑拆贡献:枚举跳跃的某一步 \(l\to r(r>l)\),设其单次得分为 \(C(l,r)\),包含它的跳跃方案总数为 \(T(l,r)\),答案显然为 \(\sum\limits_{0\leq l<r\leq n}C(l,r)T(l,r)\)。显然若 \(l\)\(r\) 是障碍物,或 \([l,r]\) 内包含超过 \(S\) 个障碍物,\(C(l,r)=0\),否则 \(C(l,r)=(r-l)^2\);设 \(x\)\([l,r]\) 以外的非障碍物且非 \(0/n\) 的点数,这些点随意选不选,于是 \(T(l,r)=2^x\)

注意到若 \([l,r]\) 内包含的障碍物数量确定了,则 \(C(l,r)\)\(T(l,r)\) 容易用较为简洁的式子写出。于是考虑枚举 \([l,r]\) 跨越的障碍物区间(该区间长度不超过 \(S\))。可以预见到,跨越至少一个障碍物和不跨越障碍物的情况有很大区别,需要分开来算。以及由于 \(0,n\) 一定要选,为了方便可以暂时将它们设为障碍物(令 \(p_0=0,p_{m+1}=n\)),最后再加上以 \(0/n\) 为一端的贡献。

考虑跨越障碍物区间 \(p_{a\sim b}(a\leq b)\) 的总贡献,显然是

\[\begin{aligned} F(a,b)&=\sum_{i=p_{a-1}+1}^{p_a-1}\sum_{j=p_b+1}^{p_{b+1}-1}2^{n+1-(m+2)+(b-a+1)-(j-i+1)}(j-i)^2\\ &=2^{n+1-m-2+b-a}\sum_{i=p_{a-1}+1}^{p_a-1}\sum_{j=p_b+1}^{p_{b+1}-1}2^{i-j}(j-i)^2 \end{aligned} \]

我们至少要 \(\mathrm O(1)\) 计算这玩意。注意到后面这一坨 \(\sum\) 可以通过二维差分,归约到以下形式:

\[\begin{aligned} &g(x,y)=\sum_{i=1}^x\sum_{j=1}^y2^{i-j}(j-i)^2\\ &f(l_1,r_1,l_2,r_2)=g(r_1,r_2)-g(r_1,l_2-1)-g(l_1-1,r_2)+g(l_1-1,l_2-1)\\ &F(a,b)=2^{n+1-m-2}2^{b-a}f(p_{a-1}+1,p_a-1,p_b+1,p_{b+1}-1) \end{aligned} \]

根据数学直觉,容易预见到:\(g(x,y)\) 可以通过若干次平凡的错相相消法得到 \(\mathrm O(1)\) 的计算式。此处直接使用 WolframAlpha 给出的结果。但考虑到在 ACM 比赛中无法使用 WolframAlpha,本篇题解末尾将会给出推导过程。

\[\begin{aligned} g(x,y)&=\textcolor{red}{2^xx^2}\textcolor{green}{(2^y-1)2^{1-y}}\\ &+\textcolor{red}{(2^x-1)}\textcolor{green}{\!\left(-y^2-6y+13(2^y-1)\right)2^{1-y}}\\ &+\textcolor{red}{2^{x+1}x}\textcolor{green}{(y-3\times 2^y+3)2^{1-y}} \end{aligned} \]

此时直接枚举障碍物区间 \([a,b]\),使用快速幂即可将这一部分做到 \(\mathrm O\!\left(m^2\log n\right)\) 的复杂度。继续优化。

观察到 \(g(x,y)\) 可以写成三项 \(h_1'(x)h_2'(y)\) 相加的形式,算上 \(2^{b-a}\) 的贡献,令 \(h_1(x)=2^{-a}h_1'(x),h_2(y)=2^{b}h_2'(y)\)。考虑三项分别计算贡献,然后相加。观察到 \(F(a,b)\) 转化到的四个 \(g(\cdot,\cdot)\) 中,后者 \(h_1(x)\) 仅与 \(a\) 有关,\(h_2(y)\) 仅与 \(b\) 有关。于是考虑扫描线:从左往右枚举 \(y\),时刻记录合法的 \(x\)\(h_1(p_x-1)\) 之和 \(s\),与 \(h_1(p_{x-1})\) 之和 \(t\),然后通过 \(u=h_2(p_{y+1}-1),v=h_2(p_y)\) 可以计算出三项中的当前这项对 \(2^{b-a}f(p_{x-1}+1,p_x-1,p_y+1,p_{y+1}-1)\) 的贡献:\(su-sv-tu+tv=(s-t)(u-v)\)。注意还要乘以一个 \(2^{n+1-m-2}\) 的常系数。

如何时刻维护合法的 \(x\)\(h_1(R(x))\) 之和与 \(h_1(L(x))\) 之和?合法的 \(x\) 是一段区间 \([\max(1,y-S+1),y]\),每次右端点 \(+1\),左端点可能不变可能 \(+1\),直接加上 / 扣掉相关贡献就行了。这样算上快速幂,复杂度是 \(\mathrm O(m\log n)\)。由于该算法有十几倍常数,使用快速幂可能会被卡常。但注意到这里面只要求 \(2\) 的若干次幂,可以使用光速幂做到 \(\mathrm O(1)\):设 \(B=\left\lceil\sqrt P\right\rceil\),对所有 \(i\in[0,B)\) 预处理 \(2^i\)\(2^{iB}\),即可 \(\mathrm O(1)\) 询问 \(2^x(x\in[0,P))\)(对于 \(x\notin[0,P)\) 显然可以用费马小定理归约到这个区间)。这样该部分复杂度就是 \(\mathrm O\!\left(m+\sqrt P\right)\)

剩下两个较为平凡的部分:不跨越任何障碍物的贡献、以 \(0/n\) 为一端的贡献。先考虑后者。以 \(0\) 为左端为例,\(n\) 为右端类似。枚举跨越的最后一个障碍物 \(a\in[0,S]\),贡献显然是

\[\sum_{i=p_a+1}^{p_{a+1}-1}2^{n-m-1-i+a}i^2=2^{n-m-1+a}f(0,0,p_a+1,p_{a+1}-1) \]

直接调用 \(f\) 函数就行了。还有一个小坑:当 \(s=m\) 时,\(T(0,n)\neq 0\),要额外算上 \(0\to n\) 的贡献。

最后是不跨越任何障碍物的贡献。枚举位置在 \(p_a+1\sim p_{a+1}-1(a\in[0,m])\) 之间,设 \(x=p_{a+1}-p_a-1\),贡献显然为

\[\sum\limits_{i=1}^x(x-i)2^{n+1-m-2-(i+1)}i^2=2^{n+1-m-2-1}\sum_{i=1}^x(x-i)2^{-i}i^2 \]

这是个比 \(g(x,y)\) 简单得多的式子,此处直接给出 WolframAlpha 的结果,推导过程从略。

\[\sum_{i=1}^x 2^{-i} i^2 (x - i) = 2^{1 - x} (x^2 + 3 (2^x + 2) x - 13 (2^x - 1)) \]

直接计算即可,复杂度 \(\mathrm O(m)\)。另外,如果不想再推式子,而想沿用之前推导得到的 \(f(\cdot,\cdot,\cdot,\cdot)\) 的结果,此处有一种带 \(\log\) 的方法。虽然有可能被卡常,但笔者认为较有启发性。这个式子其实就是 \(\sum\limits_{1\leq l<r\leq x}2^{l-r}(r-l)^2\)。令 \(M=\lfloor x/2\rfloor\),考虑分治成 \([1,M],[M,x]\) 再加上左右两部分的互相贡献(显然是 \(f(1,M,M,x)\))。若 \(x\) 是偶数,则 \([1,M],[M,x]\) 长度相等,答案也显然相等,所以可以直接归约到 \(2f(M)+f(1,M,M,x)\)\(x\) 是奇数怎么办呢?可以令 \(x\gets x-1\),即 \(f(x-1)+f(1,1,2,x)\)。这样递归层数为 \(\mathrm O(\log n)\),总复杂度 \(\mathrm O(m\log n)\)。这其实是一个类似倍增快速幂的过程。类似光速幂的方法,这个做法也容易被优化到 \(\mathrm O\!\left(m+\sqrt P\right)\)

最终总时间复杂度、空间复杂度 \(\mathrm O\!\left(m+\sqrt P\right)\),下面是标程:

#include <bits/stdc++.h>
using namespace std;
#define REP(i, l, r) for(int i = (l); i <= (r); ++i)
using ll = long long;

template<class T = int> T read() {
  T x = 0; char c = getchar(); bool ne = false;
  while(!isdigit(c)) ne |= c == '-', c = getchar();
  while(isdigit(c)) x = 10 * x + (c ^ 48), c = getchar();
  return ne ? -x : x;
}

constexpr int P = 1e9 + 7;
void addto(int &x, int y) { x += y, x >= P && (x -= P), x < 0 && (x += P); }
int add(int x, int y) { return x < 0 && (x += P), x += y, x >= P ? x - P : x < 0 ? x + P : x; }
struct power_t {
  static constexpr int B = 32768;
  int pw0[B | 10], pw1[B | 10];
  power_t() {
    pw0[0] = pw1[0] = 1;
    REP(i, 1, B - 1) pw0[i] = add(pw0[i - 1], pw0[i - 1]);
    int base = add(pw0[B - 1], pw0[B - 1]);
    REP(i, 1, B - 1) pw1[i] = (ll)pw1[i - 1] * base % P;
  }
  int operator()(ll x) {
    x = (x % (P - 1) + P - 1) % (P - 1);
    return (ll)pw1[x >> 15] * pw0[x & (B - 1)] % P;
  }
} power;

constexpr int N = 2e6 + 10;

int n, m, S;
int p[N];
int ans = 0;

int g(int x, int y) {
  return (ll)add((ll)power(x) * x % P * x % P * add(power(y), -1) % P,
             add((ll)add(power(x), -1) * add(-(ll)y * y % P, add(-6ll * y % P, 13ll * add(power(y), -1) % P)) % P,
                 (ll)power(x + 1) * x % P * add(y, add(-3ll * power(y) % P, 3)) % P)) * power(1 - y) % P;
}
int f(int l1, int r1, int l2, int r2) {
  return add(g(r1, r2), add(-g(r1, l2 - 1), add(-g(l1 - 1, r2), g(l1 - 1, l2 - 1))));
}

void solve_different_block() {
  int s1 = 0, s2 = 0, s3 = 0, t1 = 0, t2 = 0, t3 = 0;
  auto insl = [&](int coef, int x, int &a, int &b, int &c) {
    addto(a, (ll)coef * power(x) % P * x % P * x % P);
    addto(b, (ll)coef * add(power(x), -1) % P);
    addto(c, (ll)coef * power(x + 1) % P * x % P);
  };
  int u1 = 0, u2 = 0, u3 = 0, v1 = 0, v2 = 0, v3 = 0;
  auto insr = [&](int y, int &a, int &b, int &c) {
    a = (ll)add(power(y), -1) * power(1 - y) % P;
    b = (ll)add(-(ll)y * y % P, add(-6ll * y % P, 13ll * add(power(y), -1) % P)) * power(1 - y) % P;
    c = (ll)add(y, add(-3ll * power(y) % P, 3)) * power(1 - y) % P;
  };
  auto addans = [&](int coef, int s, int t, int u, int v) {
    addto(ans, (ll)coef * add(s, -t) % P * add(u, -v) % P);
  };
  REP(i, 1, m) {
    int coef = power(-i);
    insl(coef, p[i] - 1, s1, s2, s3), insl(coef, p[i - 1], t1, t2, t3);
    if(i > S) {
      coef = -power(-(i - S));
      insl(coef, p[i - S] - 1, s1, s2, s3), insl(coef, p[i - S - 1], t1, t2, t3);
    }
    insr(p[i + 1] - 1, u1, u2, u3), insr(p[i], v1, v2, v3);
    coef = power((ll)n + 1 - m - 2 + i);
    addans(coef, s1, t1, u1, v1), addans(coef, s2, t2, u2, v2), addans(coef, s3, t3, u3, v3);
  }
}

void solve_inside_block() {
  /* O(m log n) method
  function<int(int)> calc = [&](int x) {
    if(x <= 1) return 0;
    if(x & 1) {
      return add(calc(x - 1), f(1, 1, 2, x));
    } else {
      int mid = x >> 1;
      return add(f(1, mid, mid + 1, x), 2 * calc(mid) % P);
    }
  };
  REP(i, 0, m) {
    int len = p[i + 1] - p[i] - 1;
    int coef = power((ll)n + 1 - m - 2 - 1);
    addto(ans, (ll)coef * calc(len) % P);
  }
  */
  REP(i, 0, m) {
    int x = p[i + 1] - p[i] - 1;
    int fx = (ll)power(1 - x) * add((ll)x * x % P, add(3ll * add(power(x), 2) * x % P, -13ll * add(power(x), -1) % P)) % P;
    int coef = power((ll)n + 1 - m - 2 - 1);
    addto(ans, (ll)coef * fx % P);
  }
}

void solve_0_n() {
  REP(i, 0, S) {
    int coef = power((ll)n - m - 1 + i);
    addto(ans, (ll)coef * f(0, 0, p[i] + 1, p[i + 1] - 1) % P);
    addto(ans, (ll)coef * f(p[m - i] + 1, p[m - i + 1] - 1, n, n) % P);
  }
  if(S == m) addto(ans, (ll)n * n % P);
}

int main() {
  n = read(), m = read(), S = read();
  REP(i, 1, m) p[i] = read();
  p[0] = 0, p[m + 1] = n;
  solve_different_block();
  solve_inside_block();
  solve_0_n();
  cout << ans << "\n";
  return 0;
}


附:\(g(x,y)\) 的推导

\(A(a,x)=\sum\limits_{i=1}^xa^i\)\(B(a,x)=\sum\limits_{i=1}^xa^ii\)\(C(a,x)=\sum\limits_{i=1}^xa^ii^2\),其中 \(a\neq 1\)

  • \(aA(a,x)-A(a,x)=a^{x+1}-a\),解得 \(A(a,x)=\dfrac{a^{x+1}-a}{a-1}\)
  • \(aB(a,x)-B(a,x)=a^{x+1}x-\sum\limits_{i=1}^xa^i=a^{x+1}x-A(a,x)\),解得 \(B(a,x)=\dfrac{a^{x+2}x-a^{x+1}(x+1)+a}{(a-1)^2}\)
  • \(aC(a,x)-C(a,x)=a^{x+1}x^2-\sum\limits_{i=1}^xa^i(2i-1)=a^{x+1}x^2-2B(a,x)+A(a,x)\),解得 \(C(a,x)=\dfrac{a^{x+3}x^2+a^{x+2}(-2x^2-2x+1)+a^{x+1}(x+1)^2-a^2-a}{(a-1)^3}\)

于是

\[\begin{aligned} g(x,y)&=\sum_{i=1}^x\sum_{j=1}^y2^{i-j}(j-i)^2\\ &=\sum_{i=1}^x2^i\sum_{j=1}^y2^{-j}(i^2-2ij+j^2)\\ &=\textcolor{red}{C(2,x)}\textcolor{green}{A(1/2,y)}\textcolor{red}{-2B(2,x)}\textcolor{green}{B(1/2,y)}\textcolor{red}{+A(2,x)}\textcolor{green}{C(1/2,y)} \end{aligned} \]

这样也将 \(g(x,y)\) 化成了三个 \(h_1(x)h_2(y)\) 之和的形式,跟之前的式子是等效的。适当化简可以减小常数。

posted @ 2022-09-17 13:18  ycx060617  阅读(238)  评论(2编辑  收藏  举报