gym102391 2019-2020 XX Open Cup, Grand Prix of Korea J Parklife

题目:

有一个 [ 1 , 1 0 6 ] [1,10^6] [1,106]的数轴,有 n n n个线段去覆盖这个数轴,其中第 i i i条线段的左端点为 S i S_i Si,右端点为 E i E_i Ei,有一个权值 V i V_i Vi,这些线段要么包含,要么不相交,问对于 ∀ 1 ≤ k ≤ n \forall 1 \le k \le n 1kn,在数轴上每一个长度为1的小区间被覆盖最多 k k k次的限制下能取的线段权值和的最大值。
( 1 ≤ n ≤ 250000 ) (1 \le n \le 250000) (1n250000)

题解:

首先注意到这些线段要么包含,要么不相交,这满足树形结构的条件,所以可以将这些线段作为结点建出一棵树,其中大区间为其包含的小区间的祖先。那么问题就变成了对于 ∀ 1 ≤ k ≤ n \forall 1 \le k \le n 1kn,我们选择一些结点染色,问所有从根到叶子结点的路径上被染色的结点数量不超过 k k k的情况下的染色结点权值和的最大值。
这个问题可以用 d p dp dp来写,但复杂度是 O ( n 2 ) O(n^2) O(n2)的,过不了。
可以发现 k k k的最优取法一定完全包含 k − 1 k-1 k1的最优取法,相当于是在 k − 1 k-1 k1的最优取法的基础上再覆盖一层,由于对于每个 k k k我们都是贪心地取的,所以第1层取完后,第2层覆盖的就是在除去第1层选取的线段后再贪心地取一次,依此类推。所以如果我们能够求出每一层的最优解,那么答案就累加一下就行了。考虑令每个结点 u u u维护包含以 u u u为根的子树中覆盖一层的最优解,除去最优解的解集后的最优解……的集合。那么这个东西是可以递推的,首先叶子结点的集合中只有自己的权值,现在假设 u u u的所有子结点的集合都求出来了,那么 u u u的集合就是将 u u u的所有子结点的集合中的最大值加一起,次大值加一起……,还有一种情况就是选取了 u u u,所以再将 u u u的权值单独加进集合。这个集合可以用优先队列维护,从叶子结点开始向上合并,合并的复杂度和集合的大小有关,所以用启发式合并,小的并到大的。

复杂度: O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=250005;
const int maxm=1e6+5;
const int LIM=1000000;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,tp,tot,fp;
vector<int>g[maxm];
vector<ll>tmp;
int val[maxn],ptr[maxn];
priority_queue<ll>pq[maxn];
struct segment{
	int l,r,v;
}a[maxn];
int cmp(segment &a,segment &b){
	if(a.l==b.l){
		return a.r-a.l>b.r-b.l;
	}
	return a.l<b.l;
}
void build(int u,int L,int R){
	++tp;
	while(tp<=n&&L<=a[tp].l&&a[tp].r<=R){
		val[++tot]=a[tp].v;
		g[u].pb(tot);
		build(tot,a[tp].l,a[tp].r);
	}
}
int merge(int a,int b){
	if(sz(pq[a])<sz(pq[b]))swap(a,b);
	tmp.clear();
	while(sz(pq[b])){
		ll u=pq[a].top(),v=pq[b].top();
		pq[a].pop();pq[b].pop();
		tmp.pb(u+v);
	}
	for(auto v:tmp)pq[a].push(v);
	return a;
}
void dfs(int u){
	if(sz(g[u])==0){
		++fp;
		pq[fp].push(val[u]);
		ptr[u]=fp;
		return;
	}
	for(auto v:g[u]){
		dfs(v);
	}
	int p=ptr[g[u][0]];
	for(int i=1;i<sz(g[u]);i++){
		p=merge(p,ptr[g[u][i]]);
	}
	if(u)pq[p].push(val[u]);
	ptr[u]=p;
}
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].v);
	}
	sort(a+1,a+n+1,cmp);
	tp=0;
	tot=0;
	build(tot,1,LIM);
	fp=0;
	dfs(0);
	int p=0;
	ll sum=0;
	while(sz(pq[ptr[0]])){
		++p;
		sum+=pq[ptr[0]].top();
		printf("%lld ",sum);
		pq[ptr[0]].pop();
	}
	for(int i=p+1;i<=n;i++){
		printf("%lld ",sum);
	}
	puts("");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值