Heavy Intervals

Pinely Round 3 (Div. 1 + Div. 2)
C. Heavy Intervals
题目链接

题意:

给你 n n n 和长度为 n n n l , r , c l,r,c lrc 数组,每次从 l , r , c l,r,c lrc 数组分别拿出一个数,拿过的数不能再拿,要求对第 i i i 个组合 l i < r i l_i<r_i li<ri,一共选出 n n n 对,总的代价就是 ∑ i = 1 n c i ∗ ( r i − l i ) \sum_{i=1}^{n} c_i*(r_i-l_i) i=1nci(rili)。问最小代价。保证一定有解

思路:

好题,想了好几个小时做不出来,最后瞎蒙的做法过了。

具体的做法是把 l l l r r r 的数放在数轴上(想象的),相邻的 l r lr lr 合并取出组成一对,一直取完,一共 n n n 对,然后按 l e n = r − l len=r-l len=rl (区间宽度)排序,大宽度乘小 c c c,一一对应乘起来再求和,结果就是最小的。至于怎么取这 n n n l r lr lr,用个栈就可以了。

重点是证明:先看最简单的情况,假如数轴上有四个点 l 1 < l 2 < r 1 < r 2 l_1<l_2<r_1<r_2 l1<l2<r1<r2,分成长度分别为a、b、c的三段,不妨设a>c

这里:

l l l 数组就是 l 1 l_1 l1 l 2 l_2 l2

r r r 数组就是 r 1 r_1 r1 r 2 r_2 r2

再给出 c c c 数组 c 1 c_1 c1 c 2 c_2 c2,设 c 1 > c 2 c_1>c_2 c1>c2

其实无非就两种组合方式,即 l 1 , r 2 , c 2 l_1,r_2,c_2 l1,r2,c2 l 2 , r 1 , c 1 l_2,r_1,c_1 l2,r1,c1 以及 l 1 , r 1 , c 2 l_1,r_1,c_2 l1,r1,c2 l 2 , r 2 , c 1 l_2,r_2,c_1 l2,r2,c1
这样两个结果分别是:

组合1:
=b*c1+(a+b+c)*c2
=b*(c1+c2)+(a+c)*c2
组合2:
=(a+b)c2+(b+c)*c1
=b*(c1+c2)+a*c2+c*c1

可以发现其实不管怎么分,abc每一块的块数是不变的。更广泛的说, l r l r lrlr lrlr 这种和 l l r r llrr llrr 这种情况都可以这样证明,这两种拼接方式就可以推出所有的 l r lr lr 情况了,也就说不同的分组方式不影响每一块的块数。

另外从上面的比较可以看出来, l l r r llrr llrr 这种,内层 l r lr lr 分成一组外层 l r lr lr 分成一组要比另一种方式要好。而 l r l r lrlr lrlr 这种两组之间不影响。

l l r r llrr llrr 这种,可以这样想象:每个块的高度就是块出现的次数,每次移除一层给当前最小c,你如果其中一个块不移除而是留给下一层,答案肯定不优,因为后面的c肯定更大。
请添加图片描述
请添加图片描述
看上图,假如 c 1 c_1 c1 最小,反正块数是一定的,肯定让 c 1 c_1 c1 对应尽可能多的块数,而 c 2 c_2 c2 对应尽可能少的块数,但是每次每块只能选一个,所以 c 1 c_1 c1 一定是选择和两端的 l r lr lr 组合, c 2 c_2 c2 选择和内层的 l r lr lr 组合。

综上,不管什么组合情况,都是临近的 l r lr lr 分成一对,最后得到的 n n n l r lr lr 之差就是可以乘出最小代价的组合,之后就是 n n n r − l r-l rl c c c 来组合,大区间对小c。

code:

#include <iostream>
#include <cstdio>
#include <set>
#include <algorithm>
#include <stack>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
 
inline int read(){
	int x;
	scanf("%d",&x);
	return x;
}
int T,n;
int C[maxn],t[maxn],ct;
 
pair<int,bool> a[maxn<<1];
int cnt;
 
stack<int> s;
void make(){
	ct=0;
	for(int i=1;i<=cnt;i++){
		if(a[i].second==0){
			s.push(a[i].first);
		}
		else {
			t[++ct]=a[i].first-s.top();
			s.pop();
		}
	}
	sort(t+1,t+ct+1);
}
 
int main(){
	cin>>T;
	while(T--){
		cin>>n;
		cnt=0;
		for(int i=1;i<=n;i++)
			a[++cnt]=make_pair(read(),0);
		for(int i=1;i<=n;i++)
			a[++cnt]=make_pair(read(),1);
		for(int i=1;i<=n;i++)
			C[i]=read();
		sort(a+1,a+cnt+1);
		sort(C+1,C+n+1,greater<int>());
		
		make();
		
		ll ans=0;
		for(int i=1;i<=n;i++)
			ans+=1ll*t[i]*C[i];
		cout<<ans<<endl;
	}
	return 0;
}
/*
 
1
2
50 0
49 51
100 1
 
*/
  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值