题解 CF601E A Museum Robbery
作者:互联网
分析
第一道线段树分治题祭。
线段树分治呢,处理的好像就是这些基于时间上的物品加入删除问题,我们将所有事件离线处理,每一个询问代表一个时刻,对于每一个物品,处理它所存在的时段,概括一下就是用线段树来处理每一个物品能对哪些询问起作用。
所以线段树每一个节点就代表了一个时段,而在询问中我们通常是找某一个时间点,所以类似于标记永久化的思想,在从上到下的过程中,如果能覆盖一个区间,那区间内的点也能同样造成影响,所以我们先将每一个物品加到它对应的区间,用 vector 存储完全覆盖某一区间的物品有哪些。
然后以此题为例,在从上到下的过程中将包含整个区间的物品从线段树中提出,再进行背包问题的处理,再将背包数组传给左右两区间,继续分治下去。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ls rt<<1
#define rs rt<<1|1
inline void read(int &res){
res=0;
int f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=(res<<1)+(res<<3)+c-48,c=getchar();
res*=f;
}
const int base=1e7+19,mod=1e9+7;
int n,k,q,ti;
int csf[1005];
int l[15005],r[15005],v[15005],w[15005],cnt;
vector<int>V[120005];
void modify(int rt,int l,int r,int L,int R,int k){
if(L>R)return;
if(l>=L&&R>=r){
V[rt].push_back(k);
return;
}
int mid=(l+r)>>1;
if(mid>=L)modify(ls,l,mid,L,R,k);
if(mid<R)modify(rs,mid+1,r,L,R,k);
}
void solve(int rt,int l,int r,int f[]){
for(int i=0;i<V[rt].size();i++){
int x=V[rt][i];//提取物品
for(int j=k;j>=w[x];j--){//01背包
f[j]=max(f[j],f[j-w[x]]+v[x]);
}
}
if(l==r){
if(l==1)return;//起始点,因为ti初始为1线段树会方便一点,所以在这里多了一个特判
int b=1,sum=0;
for(int i=1;i<=k;i++){
sum=(sum+f[i]*b)%mod;
b=b*base%mod;
}
printf("%lld\n",sum);
return;
}
int mid=(l+r)>>1;
int g[k+1];
for(int i=0;i<=k;i++)g[i]=f[i];
//这里下去会直接在子任务中修改f数组,所以不能直接将f传给右分治任务,所以新建g以存储
solve(ls,l,mid,f);
solve(rs,mid+1,r,g);
}
signed main()
{
read(n);read(k);
cnt=n;
ti=1;
for(int i=1;i<=n;i++){
read(v[i]);read(w[i]);
l[i]=ti;//起始时间
}
read(q);
while(q--){
int op,x,y,z;
read(op);
if(op==1){
read(x);read(y);
v[++cnt]=x;w[cnt]=y;
l[cnt]=ti+1;//前面那个询问它管不到,所以+1
}
else if(op==2){
read(x);
r[x]=ti;
}
else ti++;//多一询问,时间点++
}
for(int i=1;i<=cnt;i++){
if(r[i])modify(1,1,ti,l[i],r[i],i);
else modify(1,1,ti,l[i],ti,i);//寻找该物品所能完全覆盖的每一个最大区间,加入对应vector
}
solve(1,1,ti,csf);//开始分治
}
标签:return,int,题解,Museum,mid,区间,CF601E,物品,线段 来源: https://www.cnblogs.com/zxhmh/p/15698336.html