把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P3780 [SDOI2017]苹果树

题面传送门
首先转化一下题面:一条链可以免费选一次,其余选k次,选到的最大价值。
因为价值总是>0,因此肯定是选到叶子最优。
然后我们发现这条链上还剩了一些点,这些点看上去可以排序以后贪心,但是O(nk)再多个log就非常劲爆。
有一种奇技淫巧就是将这些点拆成一个1a1,然后将a1挂在1这个点下面,这样满足依赖关系,并且将剩下的点分到了两边的点。
我们考虑在dfs的时候记录出栈顺序,这样我们可以发现,在一条到叶子的链的左边的点,恰好是叶子在这个序列中左边的点。
树上依赖背包有经典的dfs序方法:如果一个点不选直接跳过一段子树,否则一个一个选。
多重背包的经典优化方法是单调队列,因此我们可以结合单调队列求出一条链左半部分和右半部分的dp值。
然后我们在枚举每个叶子的时候,可以枚举左半边的个数,然后加上这条链的值就可以合并了。
时间复杂度O(nk)
code:

#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N (500000+5)
#define M (51000000+5)
#define K (6)
#define mod 1000000007
#define Mod (mod-1)
#define eps (1e-9)
#define ull unsigned ll
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (1ll*rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;vector<int> S[N];
int n,k,x,y,z,Dh,A[N],B[N],F1[M],F2[M],Q[N],H,T,D1[N],D2[N],B1[N],B2[N],Si[N],Ans,In[N];
I void dfs1(int x){Si[x]=1;for(int i:S[x]) dfs1(i),Si[x]+=Si[i];D1[B1[x]=++Dh]=x;}
I void dfs2(int x){reverse(S[x].begin(),S[x].end());for(int i:S[x]) dfs2(i);D2[B2[x]=++Dh]=x;reverse(S[x].begin(),S[x].end());}
I void GA(int x,int w){w+=B[x];if(!In[x]&&x<=n)for(int j=0;j<=k;j++)/* cerr<<x<<' '<<w<<' '<<F1[d(B1[x]-1,j)]<<' '<<F2[d(B2[x]-(A[x+n]?2:1),k-j)]<<'\n',*/Ans=max(Ans,w+F1[d(B1[x]-1,j)]+F2[d(B2[x]-(A[x+n]?2:1),k-j)]);for(int i:S[x]) GA(i,w);}
I void Solve(){
	int i,j;scanf("%d%d",&n,&k);for(i=1;i<=n;i++) In[i]=0,scanf("%d%d%d",&x,&A[i],&B[i]),A[i+n]=0,A[i]>1&&(A[i+n]=A[i]-1,S[i].PB(i+n),A[i]=1,B[i+n]=B[i]),x&&(S[x].PB(i),In[x]++);
	dfs1(1);Dh=0;dfs2(1);for(i=0;i<=k;i++) F1[d(0,i)]=F2[d(0,i)]=0;for(i=1;i<=Dh;i++) {
		for(j=0;j<=k;j++) F1[d(i,j)]=F1[d(i-Si[D1[i]],j)];Q[H=T=0]=0;for(j=1;j<=k;j++){
			while(H<=T&&Q[H]+A[D1[i]]<j) H++;F1[d(i,j)]=max(F1[d(i,j)],F1[d(i-1,Q[H])]+(j-Q[H])*B[D1[i]]);while(H<=T&&F1[d(i-1,Q[T])]-Q[T]*B[D1[i]]<F1[d(i-1,j)]-B[D1[i]]*j) T--;Q[++T]=j;
		}
		for(j=0;j<=k;j++) F2[d(i,j)]=F2[d(i-Si[D2[i]],j)];Q[H=T=0]=0;for(j=1;j<=k;j++){
			while(H<=T&&Q[H]+A[D2[i]]<j) H++;F2[d(i,j)]=max(F2[d(i,j)],F2[d(i-1,Q[H])]+(j-Q[H])*B[D2[i]]);while(H<=T&&F2[d(i-1,Q[T])]-Q[T]*B[D2[i]]<F2[d(i-1,j)]-B[D2[i]]*j) T--;Q[++T]=j;
		}
	}Ans=-1e9;GA(1,0);printf("%d\n",Ans);for(i=1;i<=n;i++) S[i].clear();Dh=0;
}
int main(){
	freopen("1.in","r",stdin);
	int T;scanf("%d",&T);while(T--) Solve(); 
}```
posted @   275307894a  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示