[题解] CF1327F AND Segments
有 个限制 。
要计算满足以下条件的长度为 的序列 的数量:
- 。
- 。
。
首先要想到分别计算二进制下每一位的方案数然后乘起来就是答案。
然后现在问题就变成了有若干个限制表示 在 中的数的按位与为 0/1,要计算 01 序列 的数量。
将每个限制挂到右端点上,然后 dp 计算方案数。
因为是按位与,所以值只与最后一个 0 的位置有关。所以设 表示考虑了前 个位置,最后一个 0 的位置为 时的方案数。
可以发现,对于每条限制,其实是限制了最后一个 0 的位置一定是在一个区间里。具体地说,如果有一个限制 ,就说明在 一定有一个 0,也就是当 时,最后一个 0 一定是在 中;如果有一个限制 ,就说明在 中一定全是 1,也就是当 时,最后一个 0 一定是在 中。
转移就是先考虑没有限制的时候:对当前位置填 0 还是填 1 分别考虑:
再考虑加上限制:
- 如果有一个限制 :
- 如果有一个限制 :
把第一维滚掉之后我们要做的就是单点改、前缀推平成 0、求前缀和,这个记一下前缀和和最长前缀推平长度就行了。
时间复杂度 。
constexpr int MAXN = 5e5 + 9;
int n, k, m, ans = 1, f[MAXN], a[MAXN];
vector<pair<int, int> > lim[MAXN];
int calc(int bit) {
for (int i = f[0] = 1; i <= n; i ++) f[i] = 0, a[i] = 0;
for (int r = 1; r <= n; r ++) for (auto [l, x] : lim[r])
if (x >> bit & 1) a[l] ++, a[r + 1] --;
for (int i = 1; i <= n; i ++) a[i] += a[i - 1];
int sum = 1, pre = 0;
for (int i = 1; i <= n; i ++) {
int pos = 0;
for (auto [l, x] : lim[i])
if ((x >> bit & 1) == 0) cmax(pos, l);
if (!a[i]) f[i] = sum, cadd(sum, f[i]);
while (pre < pos) cdel(sum, f[pre]), pre ++;
}
return sum;
}
void slv() {
n = Read<int>(), k = Read<int>(), m = Read<int>();
for (int i = 1; i <= m; i ++) {
int l = Read<int>(), r = Read<int>(), x = Read<int>();
lim[r].emplace_back(l, x);
}
for (int i = 0; i < k; i ++) cmul(ans, calc(i));
Write(ans, '\n');
return;
}
分类:
标签:
,
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步