启发式合并顾名思义就是得到启发后进行合并.
把原本合并时的(n>m) O(nlogm)->O(mlogn).本题题意那么长,说白了就是给你n段,然后询问在所有段最多重叠k次可以的最大权值是多少.
直接建树分治合并即可~
代码如下:
#include <bits/stdc++.h> using namespace std; const int N=1e6+56; typedef long long ll; struct FZ{ int l,r; ll w; }tr[N]; bool cmp(FZ a,FZ b) { if(a.l==b.l) return a.r>b.r; else return a.l<b.l; } ll ans[N];//单纯的存下答案而已. multiset<ll,greater<ll> >s[N];//s[i] 表示i管辖的区域内线段合并的第i大的值. vector<int>x[N],y;//x[i]表示i的子节点.y当个临时变量存节点的. void dfs(int u)//进行启发式合并. { for(auto v:x[u])//合并子节点. { dfs(v); if(s[u].size()<s[v].size()) swap(s[u],s[v]);//把v丢进u进行合并,启发式合并. multiset<ll,greater<ll> >S; for(auto t:s[v]) { ll start=*s[u].begin(); S.insert(start+t); s[u].erase(s[u].begin()); } for(auto t:S) { s[u].insert(t); } } if(u) s[u].insert(tr[u].w);//把自己再丢进去. } int main() { int n; scanf("%d",&n); tr[0].l=-1e9,tr[0].r=1e9; for(int i=1;i<=n;i++) { scanf("%d%d%lld",&tr[i].l,&tr[i].r,&tr[i].w); }sort(tr,tr+1+n,cmp); y.push_back(0); for(int i=1;i<=n;i++)//建树 { while(tr[y.back()].r<tr[i].r) y.pop_back(); x[y.back()].push_back(i); y.push_back(i); }dfs(0); int id=0; for(auto x:s[0]) ans[++id]=x; for(int i=1;i<=n;i++) ans[i]+=ans[i-1]; for(int i=1;i<=n;i++) printf("%lld ",ans[i]); puts(""); return 0; }