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;
}
}; 
京公网安备 11010502036488号