D. Happy New Year (状压DP + 扫描线)
题目: 传送门
题意: 有 n 个区间,一个数最多被 K 个区间 [Li, Ri] 包含让你选一些区间,使得被奇数个区间包含的数最多,输出这个最大值。
1 <= n <= 100000, 1 <= k <= 8, 1 <= Li <= Ri <= m <= 1e9
题解:一个数最多可以被 K 个区间包含,那我们对每个区间动态编号,那使用到的编号最多就是 K 个,然后每个编号对应一个二进制位,做状压DP。
#include <bits/stdc++.h> #define LL long long #define mem(i, j) memset(i, j, sizeof(i)) #define rep(i, j, k) for(int i = j; i <= k; i++) #define dep(i, j, k) for(int i = k; i >= j; i--) #define pb push_back #define make make_pair #define INF INT_MAX #define inf LLONG_MAX #define PI acos(-1) using namespace std; const int N = 1e6 + 5; int ID[N]; struct note { int x, ch, id; note(int x, int ch, int id) : x(x), ch(ch), id(id) { } }; bool cmp(note a, note b) { /// 右端点先处理 return a.x == b.x ? a.ch < b.ch : a.x < b.x; } vector < note > Q; vector < int > odd; int dp[N]; bool vis[N]; int main() { int n, m, k, l, r; scanf("%d %d %d", &n, &m, &k); rep(i, 1, n) { scanf("%d %d", &l, &r); Q.pb(note(l, 1, i)); Q.pb(note(r + 1, -1, i)); } sort(Q.begin(), Q.end(), cmp); rep(i, 0, (1 << k) - 1) {/// 预处理二进制位上奇数个1的数 dp[i] = -INF; int tmp = i; int now = 0; while(tmp) { now += tmp % 2; tmp = tmp / 2; } if(now & 1) odd.pb(i); } dp[0] = 0; int pre = 0; for(auto i : Q) { for(auto v : odd) { if(dp[v] == -INF) continue; dp[v] += i.x - pre; /// 若存在这样的状态就累加起来 } pre = i.x; if(i.ch > 0) { /// 左端点 int now = 0; while(now < k && vis[now]) now++; /// 对新加区间编号 vis[now] = 1; ID[i.id] = now; for(int j = 0; j < (1 << k); j++) { /// 新加区间,那对一些包含此区间的状态更新一下 if(((j >> now) & 1) == 0) { dp[j ^ (1 << now)] = dp[j]; } } } else { /// 右端点 int now = ID[i.id]; vis[now] = 0; ID[i.id] = -1; for(int j = 0; j <= (1 << k) - 1; j++) { /// 取消这个区间的编号, 然后, 对包含此区间的状态取max if(((j >> now) & 1) == 1) { dp[j ^ (1 << now)] = max(dp[j ^ (1 << now)], dp[j]); dp[j] = -INF; } } } } printf("%d\n", dp[0]); return 0; }
一步一步,永不停息