启发式合并顾名思义就是得到启发后进行合并.
把原本合并时的(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;
}

京公网安备 11010502036488号