4064. 【JSOI2015】套娃

24 篇文章 0 订阅
14 篇文章 0 订阅

Description

Input

Output

Sample Input

3
5 4 1
4 2 2
3 2 1

Sample Output

7

Data Constraint

Solution

我们不妨初始时设 ans = ∑ 𝐾𝑖 ∗ 𝐵𝑖 𝑛 𝑖=1 。

考虑一次两个套娃𝑖和𝑗的合并,即套娃𝑖装到套娃𝑗里面,对答案的贡献是 ans = ans − 𝐵𝑗 ∗ 𝐼𝑛𝑗 + 𝐵𝑗 ∗ (𝐼𝑛𝑗 − 𝑂𝑢𝑡𝑖)

即 ans −= 𝐵𝑗 ∗ 𝑂𝑢𝑡𝑖

那我们尝试用贪心的思想。 我们首先把𝐵𝑖由大到小排序,对于每个𝐵𝑖,找一个最大的可用的𝑂𝑢𝑡𝑗。 怎么证明它是对的呢?

现在有𝐵𝑖、𝐵𝑗、𝑂𝑢𝑡𝑘、𝑂𝑢𝑡𝑙,有(𝐵𝑖 > 𝐵𝑗),(𝑂𝑢𝑡𝑘 > 𝑂𝑢𝑡𝑙),且𝐾和𝐿都能装入𝐼和𝐽。

我们要证明

𝐵𝑖 ∗ 𝑂𝑢𝑡𝑘 + 𝐵𝑗 ∗ 𝑂𝑢𝑡𝑙 > 𝐵𝑖 ∗ 𝑂𝑢𝑡𝑙 + 𝐵𝑗 ∗ 𝑂𝑢𝑡𝑘

𝐵𝑖 ∗ (𝑂𝑢𝑡𝑘 − 𝑂𝑢𝑡𝑙) + 𝐵𝑗 ∗ (𝑂𝑢𝑡𝑙 − 𝑂𝑢𝑡𝑘) > 0

(𝐵𝑖 − 𝐵𝑗) ∗ (𝑂𝑢𝑡𝑘 − 𝑂𝑢𝑡𝑙) > 0

以上每一步均可逆。

于是这样做可以保证答案最小。 对于找一个最大的可用的𝑂𝑢𝑡𝑗,可以用平衡树或者线段树完成。 时间复杂度(𝑁𝑙𝑜𝑔𝑁)。

接下来我们就要对于每一个从大到小的Bi,找出一个最大的能填进这个套娃的另一个套娃j。

方法一:权值线段树

我们将所以的Out放进权值线段树中,对于每一个Bi,查询小于当前套娃In[i]的最大的一个Out[j]的值,这一步就是查找权值线段树的前驱,然后找到后在那个权值的位置上删除就好了。时间复杂度O(n log n)

方法二:并查集

对于每一个权值i,我们先将它的father赋值为i-1,将所有出现过的Out的father赋值成它们自己,并且记录t[i]表示权值i出现的次数,那么直接查询In[i]-1的father即它的前一个最大的Out的值,然后将出现次数-1,如果次数为0,就将它的father赋值为i-1即可。

Code1

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define I int
#define ll long long
#define F(i,a,b) for(register int i=a;i<=b;++i)
#define N 200010
using namespace std;
I n,f[N],ls[N],rs[N],tot,now,rt;
ll ans;
struct node{I x,y,z;}a[N];
inline I R(I &x){
	x=0;char ch=getchar();for(;ch<'0'||ch>'9';ch=getchar());
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);return x;
}
void cge(I &x,I l,I r,I k,I p){
	if(!x) x=++tot;f[x]+=p;
	if(l==r) return;
	I M=l+r>>1;
	if(k<=M) cge(ls[x],l,M,k,p);
	else cge(rs[x],M+1,r,k,p);
}
I pre(I x,I l,I r,I k){
	if(!f[x]) return 0;
	if(l==r) return l;
	I M=l+r>>1,t;
	if(k<=M) return pre(ls[x],l,M,k);
	else return (t=pre(rs[x],M+1,r,k))?t:pre(ls[x],l,M,k);
}
I cmp(node a,node b){return a.z>b.z;}
I main(){
	freopen("doll.in","r",stdin);
	freopen("doll.out","w",stdout);
	R(n);
	F(i,1,n){
		R(a[i].x),R(a[i].y),R(a[i].z);
		ans+=(ll)a[i].y*a[i].z;cge(rt,1,1e4,a[i].x,1);
	}
	sort(a+1,a+1+n,cmp);
	F(i,1,n){
		now=pre(1,1,1e4,a[i].y-1);
		ans-=(ll)now*a[i].z;
		if(now) cge(rt,1,1e4,now,-1);
	}
	printf("%lld\n",ans);
	return 0; 
}

Code2

#include<cstdio>
#include<algorithm>
#define I int
#define ll long long
#define F(i,a,b) for(register int i=a;i<=b;++i)
#define N 200010
using namespace std;
I n,tot,now,f[N],t[N];
ll ans;
struct node{I x,y,z;}a[N];
inline I R(I &x){
	x=0;char ch=getchar();for(;ch<'0'||ch>'9';ch=getchar());
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);return x;
}
I cmp(node a,node b){return a.z>b.z;}
I get(I x){return f[x]==x?x:f[x]=get(f[x]);}
I main(){
	freopen("doll.in","r",stdin);
	freopen("doll.out","w",stdout);
	R(n);
	F(i,1,1e4) f[i]=i-1;
	F(i,1,n){
		R(a[i].x),R(a[i].y),R(a[i].z);
		ans+=(ll)a[i].y*a[i].z;t[f[a[i].x]=a[i].x]++;
	}
	sort(a+1,a+1+n,cmp);
	F(i,1,n){
		now=get(a[i].y-1);
		ans-=(ll)now*a[i].z;
		if(now){if(--t[now]==0) f[now]=now-1;}
	}
	printf("%lld\n",ans);
	return 0; 
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值