[Cnoi2019] 青染之心

[Cnoi2019] 青染之心

题目描述:

Cirno初始有一个空的物品序列,一个大小为 \(V\) 的背包,现在你有 \(q\) 个操作,分为两种:

  • add x y : 表示加入一种体积为 \(x\), 价值为 \(y\) 的物品到序列末尾
  • erase : 表示删除序列末尾的物品

对于每个操作结束以后,你需要求出 :

假设序列中的每种物品都有无穷多个,Cirno的背包可以装下的物品最大价值和。

数据范围:

对于 \(100\%\) 的数据 $1\le q, V, x, y \le 2\times10^4 $

思路:

这个题目有点类似与之前学校模拟赛中一道题目,同样都有撤销操作。对于这种存在撤销操作的题目,我一开始的想法就是建一个操作树,然后在这个树上面处理。但是我们建完这棵树之后,会惊奇发现,对于每个操作的答案,就应该是从当前节点到根的路径上经过所有操作后的答案。

然后我们对这个操作树进行一个修改,就是我们在树上节点存的不是操作,而是物品的体积和价值。

所以题目转换为建出一颗树,然后把每个点到根节点这条路径抽出来,跑一边完全背包。显然这个题目的时间复杂度为 \(O(qV)\),但是不幸的是,如果这样做的话空间复杂度也是 \(O(qV)\) 的,这显然是不行的。

所以这个题目的瓶颈在于如何优化空间。

其实我们肯定想要优化这个空间,所以考虑滚动数组。但是你会发现,对于一个节点,只能有一个儿子不用回溯,即直接遍历下去,否则都是需要重新开一个数组。根据这个操作,我们可以想到使用重链剖分来优化这个东西。

其实这个问题就解决了。具体实现的操作,我们可以认为先遍历所有轻链,然后再遍历所有重链。这样做显然是正确的。又因为一条路径中最多经过 \(O(\log n)\) 条轻链,所以空间复杂度可以优化为 \(O(V\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define mem(a) memset(a,0,sizeof(a))
#define set(a,b) memset(a,b,sizeof(a))
#define ls i<<1
#define rs i<<1|1
#define pb(x) push_back(x)
#define pt(x) putchar(x)
#define T int t;cin>>t;while(t--)
#define rand RAND
using namespace std;
char buf[1<<20],*p1,*p2;
#define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
template<class Typ> Typ &re(Typ &x){char ch=gc(),sgn=0; x=0;for(;ch<'0'||ch>'9';ch=gc()) sgn|=ch=='-';for(;ch>='0'&&ch<='9';ch=gc()) x=x*10+(ch^48);return sgn&&(x=-x),x;}
template<class Typ> void wt(Typ x){if(x<0) putchar('-'),x=-x;if(x>9) wt(x/10);putchar(x%10^48);}
const int inf=0x3f3f3f3f3f;
const int maxn=2e4+5;
const int mod=1e9+7;
int seed = 19243;
unsigned rand(){return seed=(seed*48271ll)%2147483647;}
int Q,m,n;
stack<int>st;
int w[maxn],v[maxn],id[maxn];
vector<int>G[maxn];
void add(int u,int v){
    G[u].push_back(v);
    G[v].push_back(u);
}
int son[maxn],sz[maxn],vis[maxn];
void dfs(int u,int f){
    sz[u]=1;
    vis[u]=1;
    for(int v:G[u]){
        if(v==f)continue;
        dfs(v,u);
        sz[u]+=sz[v];
        if(son[u]==-1||sz[v]>sz[son[u]])son[u]=v;
    }
}
int dp[20][maxn],ans[maxn];
void dfs1(int u,int f,int dep,int lst){
    for(int i=0;i<=m;i++){
        dp[dep][i]=dp[lst][i];
        if(i>=w[u]){
            dp[dep][i]=max(dp[dep][i],dp[dep][i-w[u]]+v[u]);
        }
        ans[u]=max(ans[u],dp[dep][i]);
    }
    for(int v:G[u]){
        if(v==f||v==son[u])continue;
        dfs1(v,u,dep+1,dep);
    }
    if(son[u])dfs1(son[u],u,dep,dep);
}
signed main(){
    cin>>Q>>m;
    for(int i=1;i<=Q;i++){
        string op;
        cin>>op;
        if(op=="add"){
            n++;
            cin>>w[n]>>v[n];
            if(st.size())add(st.top(),n);
            st.push(n);
        }
        else{
            st.pop();
        }
        if(st.size())id[i]=st.top();
    }
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            dfs(i,0);
            dfs1(i,0,1,0);
        }
    }
    for(int i=1;i<=Q;i++)cout<<ans[id[i]]<<endl;
    return 0;
}
posted @ 2023-10-31 21:30  Candycar  阅读(26)  评论(0)    收藏  举报