「解题报告」Cover

题目大意

给定 \(m\) 个区间 \([l_i..r_i]\), 每一个区间都有一个权值 \(a_i\), 保证每个区间只有包含与不相交的关系,对于每一个 \(k=1,\ 2,\ \cdots,\ m\), 选出若干个区间,使 \([1..n]\) 中的每一个点不被超过 \(k\) 个区间覆盖,并求出最大的权值和.
\(1\le l_i\le r_i\le m,n\le300000,a_i\le10^9\)

思路

注意题目给出的一个条件:

保证每个区间只有包含与不相交的关系

所以这个区间是一个的关系。
首先建出这个树:将所有的区间按照右端点升序、左端点降序排序(或者其他方式,能建出树就可以),然后每次将区间压入一个栈中,遇到左端点大于栈顶元素的就一定满足包含关系,直接拿出即可。

接下来我们来考虑树形DP:
\(f_{i,j}\) 表示以 \(i\) 为根的子树中被不超过 \(j\) 段区间覆盖的最大权值和,那么不难推出以下递推式:

\[f_{i,j}=\max(\sum_{k\in son_i}f_{k,j},\ \sum_{k\in son_i}f_{k,j-1}+a_i) \]

复杂度 \(O(m^2)\)保证会炸
我们可以尝试来优化这个DP。

考虑 \(f_{i,j}\) 的差值 \(g_{i,j}=f_{i,j}-f_{i,j-1}\), 不难发现 \(g_{i,j}\) 是单调不增的,肯定先选最优解,感性理解下
那么我们可以用一个multiset来维护这个 \(g_{i,j}\) 然后合并时启发式合并即可。
说这么简单,怎么合并???

把这个DP式子可以进行一些转换:

\[\begin{aligned} f_{i,j}&=\max(\sum_{k\in son_i}f_{k,j},\ \sum_{k\in son_i}f_{k,j-1}+a_i)\\ &=\max(\sum_{k\in son_i}\sum_{p=1}^jg_{k,p},\ \sum_{k\in son_i}\sum_{p=1}^{j-1}g_{k,p}+a_i) \end{aligned} \]

那么当 \(j\) 增加时, \(\max\) 左面的式子每次加 \(\sum_{k\in son_i}g_{k,j}\), 右面的式子每次加 \(\sum_{k\in son_i}g_{k,j-1}\), 由于 \(g_{i,j}\) 是单调不增的,那么 \(g_{k,j}\le g_{k,j-1}\), 那么 \(\sum_{k\in son_i}g_{k,j}\le \sum_{k\in son_i}g_{k,j-1}\)
也就是说,当枚举到某一个 \(j\) 以后,右面的一定会优于左面的,假设这个分界线为 \(j\)\(j-1\) 选左面, \(j\) 选右面), 那么我们发现:

\[\begin{aligned} g_{i,j}&=f_{i,j}-f_{i,j-1}\\ &=(\sum_{k\in son_i}\sum_{p=1}^jg_{k,p}+a_i)-\sum_{k\in son_i}\sum_{p=1}^{j}g_{k,p}\\ &=a_i \end{aligned} \]

所以我们只需要把 \(a_i\) 也扔进multiset里就可以了。
在其他情况下, \(g_{i,j}\) 就等于 \(\sum_{k\in son_i}g_{k,j}\) 或者 \(\sum_{k\in son_i}g_{k,j-1}\), 两个multiset必然是有序的, 所以直接将两个multiset对应位置相加维护就可以了。

合并时可以用一个启发式合并:其实就是将数比较少multiset的合并到数比较大的multiset,合并也很简单,两个multiset不断取begin,相加扔新multiset就可以了。

结果得到一个multiset,因为是单调递减的,直接从后往前扫一遍,把前 \(k\) 个差分加起来就是 \(f_{1,k}\) 了。

复杂度 \(O(\)我不会证\()\)

反正挺玄学的

注意

  • 可以将 \(a_i\) 取负数存进去,这样结果只需要从前往后扫, 因为我不知道为什么从后往前扫扫挂了
  • 总之能别碰迭代器就别碰,不知道怎么就挂了

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 300005;
struct Node {
    int l,r,a;
}nodes[MAXN];
int n,m;
bool cmp(const Node &a,const Node &b) {
    return a.r!=b.r?a.r<b.r:a.l>b.l;
}
int fa[MAXN],dep[MAXN],son[MAXN];
vector<int> to[MAXN];
multiset<int> dfs2(int i) {
    if (to[i].size()!=0) {
        multiset<int> s;
        multiset<int>::iterator it;
        stack<int> st;
        for(int j : to[i]) {
            multiset<int> p=dfs2(j);
            if(s.size()<p.size()) swap(s,p);
            it=s.begin();
            for(int t : p) {
                st.push(*it+t);
                s.erase(it);
                it++;
            }
            while(!st.empty()) {
                s.insert(st.top());
                st.pop();
            }
        } 
        s.insert(-nodes[i].a);
        return s;
    } else {
        multiset<int> s;
        s.insert(-nodes[i].a);
        return s;
    }
}
signed main() {
    scanf("%lld%lld", &n, &m);
    for(int i=1;i<=m;i++) scanf("%lld%lld%lld", &nodes[i].l, &nodes[i].r, &nodes[i].a);
    sort(nodes+1,nodes+1+m,cmp);
    stack<int> st;
    for(int i=1;i<=m;i++) {
        while(!st.empty() && nodes[st.top()].l>=nodes[i].l) {
            fa[st.top()]=i;
            to[i].push_back(st.top());
            st.pop();
        }
        st.push(i);
    }
    for(int i=1;i<=m;i++) if(!fa[i]) to[0].push_back(i);
    multiset<int> s=dfs2(0);
    int sum=0;
    for(int i=1;i<=m;i++) {
        if (!s.empty()) {
            sum-=*s.begin();
            s.erase(s.begin());
        }
        printf("%lld ", sum);
    }
    return 0;
}
posted @ 2023-01-18 21:50  APJifengc  阅读(15)  评论(0编辑  收藏  举报