Pinely Round 3 (Div. 1 + Div. 2)
C. Heavy Intervals
题目链接
题意:
给你 n n n 和长度为 n n n 的 l , r , c l,r,c l,r,c 数组,每次从 l , r , c l,r,c l,r,c 数组分别拿出一个数,拿过的数不能再拿,要求对第 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∗(ri−li)。问最小代价。保证一定有解
思路:
好题,想了好几个小时做不出来,最后瞎蒙的做法过了。
具体的做法是把 l l l 和 r r r 的数放在数轴上(想象的),相邻的 l r lr lr 合并取出组成一对,一直取完,一共 n n n 对,然后按 l e n = r − l len=r-l len=r−l (区间宽度)排序,大宽度乘小 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 r−l 和 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
*/