题目
题目描述
有
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
n≤2×105 且
k
≤
1
0
5
k\le 10^5
k≤105 且
q
≤
1
0
5
q\le 10^5
q≤105 且
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}
c≤1018 。
思路
凡是 能买就买
都会让人想到
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<c≤2k+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 c−2k(它必然存在,除非选完都不够用)。
代码
感觉自己的码力好差……
#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;
}