为了能到远方,脚下的每一步都不能少.|

luckydrawbox

园龄:4个月粉丝:1关注:2

题解:CF2026F Bermart Ice Cream

Link\text{Link}

题意

初始时,有一个商店,编号为 11,其中没有物品。接下来有 qq 次操作,操作有 44 种:

  • 1 x1\ x:新建一个商店,编号为目前的商店数 +1+1,且该商店内的物品种类与当前商店 xx 中的相同。
  • 2 x p t2\ x\ p\ t:在商店 xx 中新建一种物品,体积为 pp,价值为 tt
  • 3 x3\ x:把商店 xx 中最早出现的一种物品删除,保证删除前 xx 内至少有一种物品。
  • 4 x p4\ x\ p:对商店 xx 中的物品做 0101 背包,求在总体积不超过 pp 时的最大总价值。

所有操作均合法,1q3×104,1p,t20001\le q\le 3\times 10^4,1\le p,t\le 2000,对于每个操作 44 输出答案。

分析

由于 p,qp,q 很大,所以不能出现背包合并,这启发我们只用加入单个物品的做法更新背包。

不妨先考虑物品构成的形态,不考虑删除,显然新加入物品时商店会继承前面的物品,这样物品之间的关系就构成了一棵树,同时,一个商店是一条从根到某个节点的链。若有删除,则相当于把链顶向下扩展,任意时刻的商店都是一条祖先-后代链,而查询就是查询这样一条链。

先不考虑细节(,我们考虑大概怎么做,可以先建出这棵树,然后离线出所有的询问,现在问题形如树上每个点都是一个物品,查询了若干条祖先-后代链的 0101 背包的点值(本来是前缀 max\max,但显然可以每次转移后取前缀 max\max 使其变成点值)。考虑枚举一个分支中心 xx,把跨过这个中心的链全部处理掉,具体的,找出这些链中深度最浅的链顶 uu,先求出从 xxxux\rightarrow u 的路径上的所有点的链的背包数组,接下来从 xx 往下延伸到所有经过 xx 的链的链底,每次都从祖先继承下来,在链底处背包合并求点值,在合理枚举分支中心的情况下,感觉每次移动都会对应一次原来的操作,所以是 O(pq)O(pq) 的,可以接受。

那么如何枚举才最优呢,我们考虑在 dfs 到点 xx 时,先递归处理子树,然后判断是否有未被处理的链的链顶为 xx,若有就以 xx 为中心做一次,显然这样是最优的,因为到父亲就处理不到这条链了。简单证明一下复杂度,假设有个商店的某两个询问不相交,分别为 u1v1,u2v2u_1\rightarrow v_1,u_2\rightarrow v_2depu1<depv1<depu2<depv2dep_{u_1}<dep_{v_1}<dep_{u_2}<dep_{v_2},那么处理 u2v2u_2\rightarrow v_2 时就处理不到 u1v1u_1\rightarrow v_1,但注意到这么下来链 u1u2u_1\rightarrow u_2 的点被插入、删除各一次,所以完全可以把 u1v1u_1\rightarrow v_1 当成一个独立开创的商店,因此每次背包都可以对应到一次操作,总复杂度为 O(pq)O(pq)

做完了?不,才刚刚开始。看起来没什么思维含量的一个题,为什么算上 unofficial 只有 44 个 AK ?因为这是一道【】的细节码农题。

可能遇到的问题有:商店中途没有物品时,链怎么处理?复制节点 11 怎么处理?

这里提供我的写法:我们把整个做法大致分为三个步骤:

  1. 建出物品树。
  2. 处理出询问。
  3. 背包求答案。

我们希望无论何时商店链都存在,所以不妨让商店链为上开下闭,即链顶的物品不会记入答案。同时给商店 11 强行塞一个物品(节点 11(p1,t1)=(0,0)(p_1,t_1)=(0,0),使商店 11 的链为 111\rightarrow 1,插入物品时动态维护商店链;另外再建一个空根 00,方便复制节点 11。查询时也是类似的做即可。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
	long long x=0,f=1;char ch=getchar();
	while(!isdigit(ch))
	{if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void write(long long x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int N=3e4+10,M=2010;
int T,q,tot,tx,ta,P=2000,lq;
vector<int>g[N];
int f[N][16],dep[N];
int p[N],t[N];
struct clain{
	int st,ed,aks;
}a[N];
int e[N],s1[N];
int id[N],ans[N],vis[N],sum[N];
void add(int fa,int x){
	g[fa].push_back(x);
	dep[x]=dep[fa]+1;
	f[x][0]=fa;
	for(int i=1;i<16;i++)f[x][i]=f[f[x][i-1]][i-1];
}
int find(int x,int fa){
	for(int i=15;i>=0;i--){
		if(dep[f[x][i]]>dep[fa])x=f[x][i];
	}
	return x;
}
vector<int>es[N],es2[N],ts[N];
int Dp[N][M];
void dfs2(int x,int fx,int *dp){
	int dp2[M];
	memcpy(dp2,dp,sizeof(dp2));
	if(x!=fx){
		for(int j=P-p[x];j>=0;j--){
			dp[j+p[x]]=max(dp[j+p[x]],dp2[j]+t[x]);
		}
		for(int j=1;j<=P;j++)dp[j]=max(dp[j],dp[j-1]);
	}
	for(auto y:es2[x]){
		ans[y]=0;vis[y]=1;
		for(int j=0;j<=a[y].aks;j++){
			ans[y]=max(ans[y],dp[j]+Dp[dep[a[y].st]][a[y].aks-j]);
		}
	}
	sum[x]=0;
	for(auto y:g[x]){
		if(sum[y]){
			memcpy(dp2,dp,sizeof(dp2));
			dfs2(y,fx,dp2);
		}
	}
}
void dfs1(int x){
	int son=x;
	for(auto y:g[x]){
		dfs1(y);
		if(es[id[y]].size()>es[id[son]].size())son=y;
		sum[x]+=sum[y];
	}
	if(id[x]!=id[son])
		while(es[id[x]].size()){
			es[id[son]].push_back(es[id[x]].back());
			es[id[x]].pop_back();
		}
	id[x]=id[son];
	for(auto y:g[x]){
		if(y==son)continue;
		while(es[id[y]].size()){
			es[id[son]].push_back(es[id[y]].back());
			es[id[y]].pop_back();
		}
	}
	while(ts[x].size()&&vis[ts[x].back()]){
		ts[x].pop_back();
	}
	if(ts[x].size()){
		int md=dep[x];
		for(auto y:es[id[x]]){
			md=min(md,dep[a[y].st]);
		}
		memset(Dp[dep[x]],0,sizeof(Dp[dep[x]]));
		memset(Dp[dep[x]+1],0,sizeof(Dp[dep[x]+1]));
		for(int y=x;dep[y]>md;){
			int fa=f[y][0];
			memcpy(Dp[dep[fa]],Dp[dep[y]],sizeof(Dp[dep[fa]]));
			for(int j=P-p[y];j>=0;j--){
				Dp[dep[fa]][j+p[y]]=max(Dp[dep[fa]][j+p[y]],Dp[dep[y]][j]+t[y]);
			}
			for(int j=1;j<=P;j++)Dp[dep[fa]][j]=max(Dp[dep[fa]][j],Dp[dep[fa]][j-1]);
			y=fa;
		}
		memcpy(Dp[0],Dp[N-5],sizeof(Dp[0]));
		dfs2(x,x,Dp[0]);
		es[id[x]].clear();
	}
}
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	q=read();
	tot=1;tx=1;
	s1[1]=e[1]=1;dep[1]=1;
	add(0,1);
	lq=0;
	while(q--){
		int op=read(),x,y,z;
		x=read();
		if(op==1){
			tot++;
			e[++tx]=e[x];s1[tx]=s1[x];
			p[tot]=p[e[tx]];
			t[tot]=t[e[tx]];
			add(f[e[tx]][0],tot);
			if(s1[x]==e[x])s1[tx]=tot;
			e[tx]=tot;
		}
		else if(op==2){
			y=read();z=read();
			tot++;
			add(e[x],tot);
			e[x]=tot;p[tot]=y;t[tot]=z;
		}
		else if(op==3){
			y=find(e[x],s1[x]);
			s1[x]=y;
		}
		else{
			y=read();
			a[++ta]=(clain){s1[x],e[x],y};
			es[e[x]].push_back(ta);
			es2[e[x]].push_back(ta);
			ts[s1[x]].push_back(ta);
			sum[e[x]]++;
		}
	}
	for(int i=1;i<=tot;i++)id[i]=i;
	for(auto y:g[0])dfs1(y);
	for(int i=1;i<=ta;i++)printf("%d\n",ans[i]);
	return 0;
}

本文作者:luckydrawbox

本文链接:https://www.cnblogs.com/luckydrawbox/p/18526414

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   luckydrawbox  阅读(23)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起