嘘~ 正在从服务器偷取页面 . . .

CF1313D Happy New Year 题解


CF1313D Happy New Year 题解

题目链接:Happy New Year

题意

做圣诞老人是一件很难的事。有时你不得不面对困难的情况。

今天圣诞老人在度假的时候,有 \(m\) 个孩子排在他面前。我们把他们从 \(1\)\(m\) 编上号。圣诞老人知道 \(n\) 个咒语。每个咒语给编号从 \(L_i\)\(R_i\) 的每个孩子一颗糖果。

题目保证如果将全部的咒语都用一次,每个孩子至多收到 \(k\) 颗糖果。

如果一个孩子得到的糖果量是偶数(可能为零),那么他(或她)会很伤心。不过,其他孩子(收到奇数个糖果的孩子)会很高兴。

请问圣诞老人最多能使多少个孩子快乐。

输入格式

The first line contains three integers of \(n\) , \(m\) , and \(k\) ( \(1 \leq n \leq 100\,000, 1 \leq m \leq 10^9, 1 \leq k \leq 8\) ) — the number of spells, the number of children and the upper limit on the number of candy a child can get if all spells are used, respectively.

This is followed by \(n\) lines, each containing integers \(L_i\) and \(R_i\) ( \(1 \leq L_i \leq R_i \leq m\) ) — the parameters of the \(i\) spell.

输出格式

Print a single integer — the maximum number of children that Santa can make happy.

题目保证了每个点至多有 \(k \le 8\) 条线段覆盖,如果仅仅看到字面意思确实很难解题。

实际上这句话告诉我们,所有的线段都可以无重叠的划分到 \(k\le 8\) 个轨道(集合)内,如下图(搬的)

每条线段在哪个轨道可以预处理出来

for(auto a : vec)
{
    if(a.op) { // 左端点
        for(int i = 0; i < 8; i++)
            if(!used[i]) { used[i] = true; ID[a.id] = i; break; }
    }else used[ID[a.id]] = false;
}

于是我们实际上可以用状压dp,即考虑每个点的第 \(i\) 条轨道是否存在线段。细节详见代码。

时间复杂度 \(\mathcal{O}(n2^k)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5 + 15))

struct node { int x, id, op; };
bool operator<(node a, node b) {
    return a.x == b.x ? a.op < b.op : a.x < b.x;
}
vector<node> vec;
int n, m, k, ID[N], f[505]; bool used[8]; 
bool check(int x) { return __builtin_popcountll(x) & 1; }
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m >> k;
    memset(f, 0xc0, sizeof(f)); f[0] = 0;
    for(int i = 1, l, r; i <= n; i++)
    {
        cin >> l >> r;
        vec.push_back({l, i, 1}); vec.push_back({r + 1, i, 0});
    }
    sort(vec.begin(), vec.end());
    for(auto a : vec)
    {
        if(a.op) {
            for(int i = 0; i < 8; i++)
                if(!used[i]) { used[i] = true; ID[a.id] = i; break; }
        }else used[ID[a.id]] = false;
    }
    for(int i = 0; i < vec.size(); i++)
    {
        int id = ID[vec[i].id];
        if(vec[i].op)
        {
            for(int S = (1 << 8) - 1; ~S; S--)
            {
                int v = (i + 1 < vec.size() ? (vec[i + 1].x - vec[i].x) : 0);
                if(!check(S)) v = 0;
                if((S >> id) & 1) f[S] = f[S ^ (1 << id)] + v;
                else f[S] = f[S] + v;
            }
        }else
        {
            for(int S = 0; S < (1 << 8); S++)
            {
                if(((S >> id) & 1) == 0)
                {
                    int v = (i + 1 < vec.size() ? (vec[i + 1].x - vec[i].x) : 0);
                    if(check(S)) { up(f[S], f[S ^ (1 << id)]), f[S] += v; }
                    else { up(f[S], f[S ^ (1 << id)]); }
                }else { f[S] = -INF; }
            }
        }
    }
    cout << f[0] << '\n';
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/xvklttvp


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录