好久没有见到这么喵喵的题目了!
考虑拆位乘法原理算答案即可。对于每一位,如果与为 \(1\) 那么有整个区间都是 \(1\),如果与为 \(0\) 就考虑记录每一个点 \(i\) 最晚必须在哪个点放一个 \(1\),记为 \(pos_i\)。这里可以在输入区间的时候计算,然后线性计算取 \(\max\) 即可,比较简单。
DP 转移非常的喵!设 \(dp_{i,j}\) 表示当前枚举到第 \(i\) 个数,最后一个 \(0\) 填在第 \(j\) 个位置上的方案数。
- \(j<pos_i\),有 \(dp_{i,j}=0\)。
- \(pos_i \le j < i\),有 \(dp_{i,j}=dp_{i-1,j}\)。(默认在第 \(i\) 个位置填 \(1\),所以不变)
- \(j=i\),若当前位置必须填 \(1\),则 \(dp_{i,j}=0\);否则有 \(dp_{i,j}=\sum\limits_{k=pos_i}^{i-1}dp_{i-1,k}\)。
考虑到所有操作都是从 \(i-1\) 到 \(i\),不妨直接在原数组上开刀。
第一个操作,直接维护一个左指针区间推成 \(0\)。
第二个操作,并没有什么变化。
第三个操作,实时记录和即可。
没了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5 + 10, Mod = 998244353;
int n, k, m, l[N], r[N], x[N], dp[N], pos[N], sum[N];
signed main() {
ios_base::sync_with_stdio(false); cin.tie(0), cout.tie(0);
cin >> n >> k >> m; int res = 1;
for (int i = 1; i <= m; ++i) cin >> l[i] >> r[i] >> x[i];
for (int bit = 0; bit < k; ++bit) {
for (int i = 1; i <= n + 1; ++i) sum[i] = pos[i] = 0;
for (int i = 1; i <= m; ++i) {
if (x[i] >> bit & 1) ++sum[l[i]], --sum[r[i] + 1];
else pos[r[i] + 1] = max(pos[r[i] + 1], l[i]);
}
for (int i = 2; i <= n + 1; ++i)
sum[i] += sum[i - 1], pos[i] = max(pos[i], pos[i - 1]);
dp[0] = 1; int tot = 1, key = 0;
for (int i = 1; i <= n + 1; ++i) {
while (key < pos[i]) (((tot -= dp[key]) %= Mod) += Mod) %= Mod, dp[key++] = 0;
dp[i] = sum[i]? 0 : tot, (tot += dp[i]) %= Mod;
}
(res *= dp[n + 1]) %= Mod;
}
return cout << res << endl, 0;
}
标签:CF1327F,int,Segments,pos,Sol,++,bit,sum,dp
From: https://www.cnblogs.com/MistZero/p/CF1327F-Sol.html