C
我们先定义两种计算:
- 前向,即给出
,计算
这个序列的和,其中序列长为
。
- 反向,即给出
,计算
这个序列的和,其中序列长为
。
然后我们可以把新牌堆分拆成上下两部分:下面 张牌,上面
张牌,分别计算这两个部分对答案的贡献。
先分析下面 张牌的成分。一开始做了
次操作后,原牌堆自底向上变成:
其中 。
然后下面的 张牌中:
- 自底向上的第
张牌,对应原牌堆自底向上的第
张牌。
- 自底向上的第
张牌,对应原牌堆自顶向下的第
张牌。
那么如果要计算自底向上 区间的牌的和,可以换回到原牌堆求解。具体而言,结果就是前向计算
和后向计算
的和。如果要计算
的和可以用前缀和减一下。
再分析上面 张牌的成分。这部分比较简单,只要算出第
张牌的值然后用前向计算即可。
由于所有运算都可以 完成,因此总的时间复杂度为
。
PS:这题不要忘了取模,比赛的时候我有一个运算忘了取模直接就炸了。。。
const long long M = 998244353; class Solution { long long m_, b1, b2; inline long long addto(long long l, long long r){ return ((l + r) * (r - l + 1) >> 1ll) % M; } inline long long modadd(long long x, long long y){ return (x + y >= M ? x + y - M: x + y); } long long forward(long long base, long long lft){ long long res = 0; if (lft >= m_ - base + 1ll){ lft -= m_ - base + 1ll, res = addto(base, m_); res = modadd(res, (lft / m_) * addto(1ll, m_) % M); lft %= m_; if (lft > 0) res = modadd(res, addto(1ll, lft)); }else { res = addto(base, base + lft - 1); } return res; } long long backward(long long base, long long lft){ long long res = 0; if (lft >= base - 1){ lft -= base - 1ll, res = addto(1, base - 1); res = modadd(res, 1ll * (lft / m_) * addto(1ll, m_) % M); lft %= m_; if (lft > 0) res = modadd(res, addto(m_ - lft + 1, m_)); }else { res = addto(base - lft, base - 1); } return res; } long long q1(long long r){ if (r == 0ll) return 0; long long hf = r >> 1; return modadd(forward(b1, r - hf), backward(b1, hf)); } long long q2(long long r){ if (r == 0ll) return 0ll; return forward(b2, r); } public: long long work(long long n, long long m, long long a, long long b, long long x, long long y) { b1 = a % m + 1, b2 = (b1 - 1 + b) % m + 1; m_ = m; long long ans = 0; if (x <= 2ll * b){ ans = modadd(ans, M - q1(x - 1)); ans = modadd(ans, q1(min(2ll * b, y))); } if (y > 2ll * b){ ans = modadd(ans, M - q2(max(2 * b, x - 1) - 2 * b)); ans = modadd(ans, q2(y - 2 * b)); } return ans; } };