昨晚 ABC F 题解
2026-01-04 23:42:01
发布于:广东
简单数数题。要是最后 30min 没写作业说不定就过了。
题意解析:定义一个长度为 的数组 的“峰”与“谷”如下:
- 若 且 ,则 为 的峰。
- 若 且 ,则 为 的谷。
定义一个数组 为山峰数组当且仅当它的峰的数量严格大于谷的数量。
给定一个 的排列 ,求 有多少个子序列为山峰数组。
我们考虑什么情况下才是山峰数组。
注意到不算两边的话,一定是“...峰谷峰谷峰谷...”状的。
再进一步,分类讨论一下,可以发现一个长度为 的数组 是山峰数组当且仅当 且 。
现在问题就简单了。
先考虑暴力。
我们可以暴力枚举 分别作为子数组的 ,如果 且 ,则不论中间是啥,都可以构成山峰数组,对答案的贡献为 (因为 时前面的是 所以要对 取最大值)。这样是 的。
然后推式子。
显然我们可以通过树状数组 预处理出 与 ,分别记作 。则原式为:
有点难搞,拆开吧。
然后继续转化。注意到 的整数次幂是可逆的。所以:
我们只需要递推求出 即可。
代码如下:
namespace cjdst{
const ll mod = 998244353;
const ll inv2 = mod / 2 + 1;
ll ksm[600005], invksm[600005];
void init(){
ksm[0] = invksm[0] = 1;
for(int i = 1; i <= 600000; i++){
ksm[i] = ksm[i - 1] * 2 % mod;
invksm[i] = invksm[i - 1] * inv2 % mod;
}
}
void solve(){
int n;
std::cin >> n;
std::vector <int> a(n + 5);
for(int i = 1; i <= n; i++){
std::cin >> a[i];
}
Fenwick_Tree <ll> tr1(n + 5), tr2(n + 5);
std::vector <ll> b(n + 5), c(n + 5), d(n + 5);
for(int i = 1; i <= n; i++){
tr2.modify(a[i], 1);
}
for(int i = 1; i <= n; i++){
tr2.modify(a[i], -1);
b[i] = tr1.query(a[i] - 1);
c[i] = tr2.query(a[i] - 1);
tr1.modify(a[i], 1);
}
for(int i = n; i; i--){
d[i] = d[i + 1] + c[i] * ksm[i];
d[i] %= mod;
}
ll ans = 0;
for(int i = 2; i < n; i++){
ans += b[i] * c[i] % mod;
ans %= mod;
}
for(int i = 1; i <= n; i++){
ans += b[i] * d[i + 1] % mod * invksm[i + 1] % mod;
ans %= mod;
}
std::cout << ans << '\n';
}
}
时间复杂度:。
全部评论 3
%%%
2026-01-05 来自 江西
1没啥用的 trick:注意到对于任意奇数 ,,所以 是 在模 意义下的乘法逆元
2026-01-04 来自 广东
0d
2026-01-04 来自 广东
0






















有帮助,赞一个