CF1515I Phoenix and Diamonds

27 篇文章 0 订阅
14 篇文章 0 订阅

Description

Solution

  • 假设手上有 r e s res res的空间,考虑每一次让 r e s res res减半,做 l o g log log次即可。
  • 考虑减半的过程,不以 r e s / 2 res/2 res/2为边界,而是以 2 k 2^k 2k为边界,从大到小枚举 k k k,将 [ 1 , 2 k ] [1,2^k] [1,2k]设为轻物品, ( 2 k , 2 k + 1 ] (2^k,2^{k+1}] (2k,2k+1]设为重物品,不妨假设 r e s ≤ 2 k + 1 res\le2^{k+1} res2k+1,需要贪心选使得 r e s ≤ 2 k res\le2^k res2k,那么最多取一个重物品,并且取这个重物品之前所有轻物品都可以取,因此计区间最小前缀和 p p p(最后一个为重物品,前面所有为这个重物品前的轻物品),如果 p ≤ r e s p\le res pres就表明可以在这个区间内取一个重物品,线段树二分下去即可。否则就尽量取轻物品。
  • 因此每一次减半需要一次线段树二分,时间复杂度 O ( n   l o g   n   l o g   w ) O(n\ log\ n\ log\ w) O(n log n log w).
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 200005
#define maxm 19
#define ll long long 
#define inf (ll)1e18+5
using namespace std;

int n,q,i,j,k,id[maxn],L;
struct arr{ll w,v,c; int i;} a[maxn];
int cmp(arr a,arr b){return a.v>b.v||a.v==b.v&&a.w<b.w;}

ll res,sum; int now;

struct val{ll w,v;};
val operator+(val a,val b){return (val){a.w+b.w,a.v+b.v};}
val operator*(val a,ll c){return (val){a.w*c,a.v*c};}
int operator<(val a,val b){return a.w<b.w||a.w==b.w&&a.v>b.v;}
struct SegmentTree{
	int lim,I[maxn*4];
	val ts[maxn*4],tm[maxn*4];
	void newnode(int x,int l){
		if (a[l].w<=lim) {
			ts[x]=(val){a[l].w,a[l].v}*a[l].c;
			tm[x]=(val){inf,0},I[x]=0;
		} else {
			ts[x]=(val){0,0};
			if ((a[l].w<=lim<<1)&&a[l].c) tm[x]=(val){a[l].w,a[l].v},I[x]=l;
			else tm[x]=(val){inf,0},I[x]=0;
		}
	}
	void upd(int x){
		ts[x]=ts[x<<1]+ts[x<<1^1];
		if (tm[x<<1]<ts[x<<1]+tm[x<<1^1])
			tm[x]=tm[x<<1],I[x]=I[x<<1];
		else tm[x]=ts[x<<1]+tm[x<<1^1],I[x]=I[x<<1^1];
	}
	void maketree(int x,int l,int r){
		if (l==r){
			newnode(x,l);return;
		}
		int mid=(l+r)>>1;
		maketree(x<<1,l,mid),maketree(x<<1^1,mid+1,r);
		upd(x);
	}
	void change(int x,int l,int r,int p){
		if (l==r){newnode(x,l);return;}
		int mid=(l+r)>>1;
		if (p<=mid) change(x<<1,l,mid,p);
		else change(x<<1^1,mid+1,r,p);
		upd(x);
	}
	void merge(int x,int l,int r,int L,int R){
		if (l>R||r<L||res<lim) return;
		if (L<=l&&r<=R){
			if (res>=tm[x].w&&I[x]){
				if (l==r) res-=tm[x].w,sum+=tm[x].v,now=l;
				else merge(x<<1,l,(l+r)>>1,L,R),merge(x<<1^1,((l+r)>>1)+1,r,L,R);
				return;
			}
			if (res>=ts[x].w) {res-=ts[x].w,sum+=ts[x].v,now=r;return;}
			else if (l==r) {ll d=res/a[l].w; res-=d*a[l].w,sum+=d*a[l].v,now=l;return;}
		}
		int mid=(l+r)>>1;
		merge(x<<1,l,mid,L,R),merge(x<<1^1,mid+1,r,L,R);
	}
} t[maxm];

int main(){
	freopen("ceshi.in","r",stdin);
	freopen("ceshi1.out","w",stdout);
	scanf("%d%d",&n,&q);
	for(i=1;i<=n;i++) scanf("%lld%lld%lld",&a[i].c,&a[i].w,&a[i].v),a[i].i=i;
	for(i=1;i<=n;i++) k=max(k,(int)a[i].w);
	sort(a+1,a+1+n,cmp);
	for(i=1;i<=n;i++) id[a[i].i]=i;
	L=0; while (1<<L<k) L++;
	for(i=0;i<=L;i++) 
		t[i].lim=1<<i,t[i].maketree(1,1,n);
	while (q--){
		int tp; scanf("%d",&tp);
		if (tp==1||tp==2){
			scanf("%d%d",&j,&k),a[id[k]].c+=(tp==1)?j:-j;
			for(i=0;i<=L;i++) t[i].change(1,1,n,id[k]);
		} else {
			sum=0,now=0; scanf("%lld",&res);
			for(i=L;i>=0&&now<n;i--) 
				t[i].merge(1,1,n,now+1,n);
			printf("%lld\n",sum);
		}
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值