CF930E。
很水的 2900,连我都想出来了。
第一步,离散化,把所有区间离散化了,因为我们只关心每个硬币对每个区间的包含关系,并不在乎它具体是哪个。
先打暴力:设 \(f_{i,j,0/1}\) 表示考虑到了第 \(i\) 个段,上个与当前硬币不同面的硬币的位置在哪个段里,还有当前段最后一个硬币的正反。
正为 \(1\),反为 \(0\)。
则易得出转移方程。
在这段里全部填 \(0\):
全部添 \(1\):
\(0,1\) 混杂,此处之所以要减一是因为你要固定最后一个的正反:
注意由于要考虑限制:如果不考虑离散化则,在 \([l,r]\) 内至少有一个 \(1\),此时考虑到第 \(i\) 个硬币,则 \(f_{r,i,0}\space(i<l)\) 是不合法的,应置为 \(0\)。
而离散化之后小于不好刻画,故我们考虑把 \(l-1\) 扔进去离散化而不是 \(l\)。
这样条件就能刻画为:对于每个状态 \(f_{r,i,0} \space (i \le l-1)\) 算完之后我们将其清零。
注意到由于只有在 \(f_i\) 与 \(f_{i+1}\) 之间转移,我们滚动数组。
滚完之后发现,除了清零之外,改变的位置个数是 \(O(1)\) 的,即 \(f_{i+1,0/1}\) 与 \(f_{i,0/1}\)。
而在这些的转移式中所有的 \(j\) 它们都会产生固定 \(1\) 或 \((2^{len-1} - 1)\) 的贡献。
这启发我们维护当前 \(f_{*,0}\) 的和与 \(f_{*,1}\) 的和。
每次将对应的贡献乘上总和加上去集合。
于是除了清零之外的操作我们都能在 \(O((n+m)\log k+(n+m)\log (n+m))\) 的时间内解决了。
那清零怎么办呢?
由于只有 \(f\) 的后边会发生变化,前边的变化只有清零,因此一个格子如果被清零它就彻底没戏了。
于是我们记录 \(f_{*,0/1}\) 的两个指针指向当前数组中第一个非零,如果要清空更多零暴力移指针就行了。
因为每清一个零指针就向后一格,而指针单调向后走所以均摊复杂度 \(O(n+m)\)。
于是我们就做完了。
小细节:
- 初始条件是 \(f_{0,0/1}=1\)。
- 离散化的时候要把 \(0,k\) 加入,因为他们是 DP 的起点与终点。
- 输出答案的时候要除以 \(2\),因为你多算了 \(0\) 号硬币的正反但压根就不存在 \(0\) 号硬币。
#include <bits/stdc++.h>
using namespace std;#define int long long
#define endl '\n'
#define debug(x) cerr << #x << " = " << x << endl
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define gn(u, v) for (int v : G.G[u])
#define pb emplace_back
#define mp make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define pii pair<int, int>
#define vi vector<int>
#define vpii vector<pii>
#define vvi vector<vi>
#define no cout << "NO" << endl
#define yes cout << "YES" << endl
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define tomin(x, y) ((x) = min((x), (y)))
#define tomax(x, y) ((x) = max((x), (y)))
#define ck(mask, i) (((mask) >> (i)) & 1)
#define pq priority_queue
#define umap unordered_map
#define FLG (cerr << "Alive!" << endl);constexpr int MAXN = 5e5 + 5;
constexpr int MOD = 1e9 + 7;
int k, n, m;
int l[MAXN], r[MAXN], sum[2], f[MAXN][2];
int c[MAXN], tot;int chk[2][MAXN], tic[2] = { -1, -1 };int qpow(int x, int y) {if (y < 0) return 0;int ans = 1;while (y) {if (y & 1)ans = ans * x % MOD;x = x * x % MOD;y >>= 1;}return ans;
}signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin >> k >> n >> m;rep (i, 1, n + m) {cin >> l[i] >> r[i];l[i]--;c[++tot] = l[i];c[++tot] = r[i];}c[++tot] = 0;c[++tot] = k;sort(c + 1, c + tot + 1);tot = unique(c + 1, c + tot + 1) - c - 1;rep (i, 1, n + m) {l[i] = lower_bound(c + 1, c + tot + 1, l[i]) - c;r[i] = lower_bound(c + 1, c + tot + 1, r[i]) - c;}memset(chk, 0xff, sizeof chk);rep (i, 1, n)tomax(chk[0][r[i]], l[i]);rep (i, n + 1, n + m)tomax(chk[1][r[i]], l[i]);f[0][0] = f[0][1] = 1;int sum[2] = { 1, 1 };rep (i, 1, tot - 1) {(f[i][0] += sum[1]) %= MOD;(f[i][1] += sum[0]) %= MOD;int cur;if (c[i + 1] - c[i] == 1) cur = 0;else (cur = qpow(2, c[i + 1] - c[i] - 1) + MOD - 1) %= MOD;(cur *= sum[0] + sum[1]) %= MOD;(f[i + 1][0] += cur) %= MOD;(f[i + 1][1] += cur) %= MOD;sum[0] = sum[1] = (sum[0] + sum[1]) % MOD;(sum[0] += cur) %= MOD;(sum[1] += cur) %= MOD;while (tic[0] < chk[0][i + 1]) {tic[0]++;(sum[0] += MOD - f[tic[0]][0]) %= MOD;}while (tic[1] < chk[1][i + 1]) {tic[1]++;(sum[1] += MOD - f[tic[1]][1]) %= MOD;}}cout << (sum[0] + sum[1]) * (MOD + 1) / 2 % MOD << endl;return 0;
}