[CF1515I]Phoenix and Diamonds

220 篇文章 2 订阅
137 篇文章 1 订阅

题目

传送门 to CF

题目描述
n n n 种物品,第 i i i 种物品的重量是 w i w_i wi,收益是 v i v_i vi

你有一个背包,装的物品的重量之和不能超过 c c c 。你会按照收益从大到小(收益相同则 w w w 从小到大)拿物品,能拿则拿。问最后你得到的收益。

事实上,有 q q q 次操作。每次操作可能让第 t t t 个物品增加 k k k 个或减少 k k k 个。然后询问你,假如你执行上面的操作,得到的收益是多少。询问不会让物品变少,这只是假设。

数据范围与提示
n ≤ 2 × 1 0 5 n\le 2\times 10^5 n2×105 k ≤ 1 0 5 k\le 10^5 k105 q ≤ 1 0 5 q\le 10^5 q105 max ⁡ ( w i , v i ) ≤ 1 0 5 \max(w_i,v_i)\le 10^5 max(wi,vi)105 但是 c ≤ 1 0 18 c\le 10^{18} c1018

思路

凡是 能买就买 都会让人想到 log ⁡ c \log c logc,容量每次至少减半。就像经典的 T - S h i r t s \rm T\text-Shirts T-Shirts 一样。

这里怎么才能让它容量减半呢?其实更像辗转相除:如果你拿一个重量超过 ⌊ c 2 ⌋ \lfloor\frac{c}{2}\rfloor 2c 的,直接减半了。否则,你想要拿不下一个重量不超过 ⌊ c 2 ⌋ \lfloor\frac{c}{2}\rfloor 2c,还是需要减半。

形式化地,我们称重量超过 ⌊ c 2 ⌋ \lfloor\frac{c}{2}\rfloor 2c 的为 h e a v y \rm heavy heavy,否则为 l i g h t \rm light light 。那么只有两种情形:

  • 拿了一个 h e a v y \rm heavy heavy,与它前面的所有 l i g h t \rm light light
  • 拿了前面很多 l i g h t \rm light light,它们的和超过了 ⌊ c 2 ⌋ \lfloor{c\over 2}\rfloor 2c

对于情形一,你要确保前面的 h e a v y \rm heavy heavy 都拿不了。即,找到最靠左的一个 h e a v y \rm heavy heavy 满足它与前面的 l i g h t \rm light light 的重量和不超过 c c c 。这个就直接单调栈,然后二分;如果带修改,就把它放到线段树上去。

然而 ⌊ c 2 ⌋ \lfloor\frac{c}{2}\rfloor 2c 是变化的,这就不太好了,因为我们需要将 h e a v y \rm heavy heavy 确定下来。事实上,我们可以先用一个 2 k    ( k = ⌊ log ⁡ 2 c ⌋ ) 2^k\;(k=\lfloor\log_2c\rfloor) 2k(k=log2c) 作为假想的 “一半”,那就只有 log ⁡ c \log c logc 个不同的 “一半” 了。就用这么多个线段树呗。

对于情形二,我们也应该求最靠左的一个,因为我们寻找的就是第一次容量减半之时。求前缀和,然后二分;如果带修改,又只好放在线段树上去了。

时间复杂度 O [ ( n + q ) log ⁡ n log ⁡ c ] \mathcal O[(n+q)\log n\log c] O[(n+q)lognlogc]

事实上,上面的说辞应当这样修改:当 2 k < c ≤ 2 k + 1 2^{k}<c\le 2^{k+1} 2k<c2k+1 时,令重量大于 2 k 2^k 2k 的为 h e a v y \rm heavy heavy,那么一个 h e a v y \rm heavy heavy 加上前面的所有 l i g h t \rm light light 这个组合,显然仍然能够使 c c c 变为 2 k 2^{k} 2k 及以下。如果没有这个组合,那就是 l i g h t \rm light light 的前缀和超过 c − 2 k c-2^k c2k(它必然存在,除非选完都不够用)。

代码

感觉自己的码力好差……

#include <bits/stdc++.h>
using namespace std;
typedef long long int_;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(int x){
	if(x > 9) writeint(x/10);
	putchar((x-x/10*10)^48);
}
struct Minimizer{
	int_ operator()(int_ a,int_ b){
		return min(a,b);
	}
};
struct Maximizer{
	int_ operator()(int_ a,int_ b){
		return max(a,b);
	}
};
struct Seconder{
	int_ operator()(const int_&,const int_ &b){
		return b; // first is useless
	}
};

const int MaxN = 200005;
const int_ infty = (1ll<<60)-1;

template < class Joiner, class Adder >
struct SgTree{
	Joiner fuck; Adder shit;
	int_ v[MaxN<<1|1];
	int_ lazy[MaxN<<1|1]; // add
	# define __index(l,r) (((l)+(r))|((l)!=(r)))
	bool bad[MaxN];
	int_& goSour(int id){
		bad[id] = true;
		return v[id<<1];
	}
	void addNode(int l,int r,int_ x){
		if(l == r && bad[l]) return ;
		v[__index(l,r)] += shit(r-l+1,x);
		lazy[__index(l,r)] += x;
	}
	inline void pushDown(int l,int r){
		int mid = (l+r)>>1;
		int_ &x = lazy[__index(l,r)];
		addNode(mid+1,r,x);
		addNode(l,mid,x); x = 0;
	}
	inline void pushUp(int l,int r){
		int mid = (l+r)>>1;
		v[__index(l,r)] = fuck(
			v[__index(l,mid)],
			v[__index(mid+1,r)]
		);
	}
	void modify(int ql,int qr,int_ qv,int l=1,int r=MaxN){
		if(qr < l || r < ql) return ;
		if(ql <= l && r <= qr)
			return addNode(l,r,qv);
		pushDown(l,r);
		modify(ql,qr,qv,l,(l+r)>>1);
		modify(ql,qr,qv,(l+r)/2+1,r);
		pushUp(l,r);
	}
	int_ query(int ql,int qr,int l=1,int r=MaxN){
		if(qr < l || r < ql) return 0;
		if(ql <= l && r <= qr)
			return v[__index(l,r)];
		pushDown(l,r); return fuck(
			query(ql,qr,l,(l+r)>>1),
			query(ql,qr,(l+r)/2+1,r)
		);
	}
	int lower_bound(int_ qv,int l=1,int r=MaxN){
		if(v[__index(l,r)] < qv) return -1;
		if(l == r) return l; else pushDown(l,r);
		int d = lower_bound(qv,l,(l+r)>>1);
		return (~d)?d:lower_bound(qv,(l+r)/2+1,r);
	}
	int upper_bound(int_ qv,int l=1,int r=MaxN){
		if(v[__index(l,r)] > qv) return -1;
		if(l == r) return l; else pushDown(l,r);
		int d = upper_bound(qv,l,(l+r)>>1);
		return (~d)?d:upper_bound(qv,(l+r)/2+1,r);
	}
	# undef __index
};

const int LogC = 30;
// when heavy weight > 2^i
SgTree< Minimizer,Seconder > one[LogC];
SgTree< Maximizer,Seconder > two[LogC];
SgTree< plus<int_>,multiplies<int_> > sumw[LogC];
SgTree< plus<int_>,multiplies<int_> > sumv[LogC];

struct Node{
	int a, w, v, id;
	bool operator < (const Node &t) const {
		if(v != t.v) return v > t.v;
		return w < t.w;
	}
};
Node node[MaxN];
int pos[MaxN], n;
void solve(){
	int_ c; scanf("%lld",&c);
	int now = 1; int_ ans = 0;
	for(int j=LogC-1; ~j&&now<=n; --j){
		if((1<<j) >= c) continue;
		int_ want = c; // no more than c
		want += sumw[j].query(1,now-1);
		int p = one[j].upper_bound(want);
		want = want-(1<<j)+1;
		int q = two[j].lower_bound(want);
		if(q < now && p < now){
			ans += sumv[j].query(now,n);
			break; // get all
		}
		if(p < now || (q >= now && p > q)){
			ans += sumv[j].query(now,q-1);
			c -= sumw[j].query(now,q-1);
			p = min(0ll+node[q].a,c/node[q].w);
			ans += 1ll*node[q].v*p;
			c -= node[q].w*p, now = q+1;
		}
		else{
			ans += sumv[j].query(now,p-1);
			c -= sumw[j].query(now,p-1);
			q = min(0ll+node[p].a,c/node[p].w);
			ans += 1ll*node[p].v*q;
			c -= node[p].w*q, now = p+1;
		}
		printf("j = %d, now = %d, ans = %lld\n",j,now,ans);
	}
	printf("%lld\n",ans);
}

int main(){
	n = readint(); int q = readint();
	for(int i=1; i<=n; ++i){
		node[i].a = readint();
		node[i].w = readint();
		node[i].v = readint();
		node[i].id = i;
	}
	sort(node+1,node+n+1);
	rep(i,1,n) pos[node[i].id] = i;
	rep(j,0,LogC-1){
		rep(i,1,n)  // sour first
			if(node[i].a <= (1<<j))
				one[j].goSour(i) = infty;
		rep(i,1,n)
			if(node[i].a > (1<<j))
				one[j].modify(i,i,1ll*node[i].a*node[i].w);
			else{
				one[j].modify(i,n,1ll*node[i].a*node[i].w);
				two[j].modify(i,n,1ll*node[i].a*node[i].w);
				sumw[j].modify(i,i,1ll*node[i].a*node[i].w);
				sumv[j].modify(i,i,1ll*node[i].a*node[i].v);
			}
	}
	for(int opt,k,d; q; --q){
		opt = readint();
		if(opt == 3){
			solve(); continue;
		}
		k = readint(), d = pos[readint()];
		if(opt == 2) k = -k;
		rep(j,0,LogC-1) // for every Segment Tree
			if(node[d].a > (1<<j))
				one[j].modify(d,d,1ll*k*node[d].w);
			else{
				one[j].modify(d,n,1ll*k*node[d].w);
				two[j].modify(d,n,1ll*k*node[d].w);
				sumw[j].modify(d,d,1ll*k*node[d].w);
				sumv[j].modify(d,d,1ll*k*node[d].v);
			}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Diamonds数据集是一个常用的数据集,包含了53,940个钻石的信息,每个钻石有10个属性:克拉数(carat)、切工(cut)、颜色(color)、净度(clarity)、深度(depth)、表宽比(table)、价格(price)、长度(x)、宽度(y)和深度(z)。这个数据集经常被用来进行数据分析和机器学习的练习,因为它包含了多个类型的特征变量,可以用来预测钻石价格或者其他的属性。 ### 回答2: diamonds数据集是一个经典的数据集,其中包含关于钻石的许多信息。这个数据集由GIA(Gemological Institute of America)收集,包含了53,940个钻石的数据。数据集的列包含了钻石的各种属性,比如克拉重量、切割质量、颜色、净度等等。 首先,数据集包含了钻石的克拉重量属性,这是一个连续的数值特征。这个特征对于了解钻石的大小和价值非常重要,是购买钻石时的一个关键指标。 另外,数据集还提供了钻石的切割质量属性,这是一个分类特征。这个特征描述了钻石的切割工艺质量,切割质量对于钻石的亮度和火彩有着直接的影响,是决定钻石品质的重要因素之一。 此外,数据集还包含了钻石的颜色属性,这也是一个分类特征。钻石的颜色分级是根据其无色到有色的程度来评定的,颜色越透明,钻石的品质越高。 最后,数据集还提供了钻石的净度属性,也是一个分类特征。净度描述了钻石的内部杂质和瑕疵程度,净度越高,钻石的品质越好。 总而言之,diamonds数据集提供了钻石的多个属性和特征,对于研究和了解钻石市场和品质有着重要的意义。研究人员和消费者可以利用这个数据集来进行统计分析、预测模型等等,从而更好地了解和选择钻石。 ### 回答3: diamonds数据集是一个广泛使用的数据集,记录了各种钻石的属性信息,包括重量、颜色、净度、切割质量和价格等。这个数据集包含了53940条数据,每条数据有10个变量。这些变量包括了以下内容: 1. Carat(克拉):钻石的重量,用于衡量钻石的大小。克拉数越高,钻石越大。 2. Cut(切割质量):钻石的切割质量,包括了五个等级:Fair、Good、Very Good、Premium和Ideal。 3. Color(颜色):钻石的颜色,从D到J共有7个等级,其中D表示最无色级别,J表示最黄色级别。 4. Clarity(净度):钻石的净度,用于衡量钻石内部的瑕疵程度。净度由FL(无瑕级别)到I1(包含显眼瑕疵)共有8个等级。 5. Depth(深度):钻石的深度,即钻石顶部到底部的距离占总高度的百分比。深度越大,钻石越深。 6. Table(台宽):钻石的台宽,即钻石顶部的平面面积占总面积的百分比。台宽越大,钻石越扁平。 7. Price(价格):钻石的价格,以美元计算。 8. X(长度):钻石的长度(mm)。 9. Y(宽度):钻石的宽度(mm)。 10. Z(深度):钻石的深度(mm)。 通过分析这个数据集,我们可以了解到钻石的各种属性对价格的影响。可以通过绘制散点图、箱线图和相关性分析来观察这些因素之间的关系。此外,我们还可以利用机器学习算法建立模型来预测钻石的价格。 总的来说,diamonds数据集是一个价值很高的数据集,可以帮助我们研究钻石的属性与价格之间的关系,并且可以用于价格预测等实际应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值