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;
}

 

posted on 2020-02-27 15:11  Willems  阅读(239)  评论(0编辑  收藏  举报

导航