[SDOI2017]苹果树
作者:互联网
此题爆肝一个晚上的菜鸡,呜呜呜
题意:luogu P3780
一棵树,每个点都有全值和取它次数的上限。h为你所取的最大深度(根为1),c为你取的个数,满足c-h<=k(k题目给出)
思路:
相当于免费取一条链(尽量长肯定到叶子),然后我么可以想到枚举这条链。
除了链上的点之外的其它点都要满足父子关系。
我一开始想到链上随便选于是枚举链上不免费选的个数,然后后贪心从大到小选,其余的点跑多重背包dp。
当然这样是有问题的,链上的点会作为父亲影响到非链上的点。
ps.树上背包复杂度是\(O(n^2)\),树上多重背包后序遍历dfs序转化后+单调队列优化也是可以\(O(n^2)\),为此我还去学了一下,当然这里是\(O(n*k)\)。
为了满足父子关系又方便搞免费链,每个点拆成2个容量分别为\(1\)和\(a_i-1\)的点,即把\(a_i-1\)挂在\(1\)的点上。
问题轻松转化为:
- 链上的免费的点(不包括挂的)
- 链左边(链叶子的dfs序前)
- 链右边 (邻接表reverse后如上)
后序遍历一下,搞出dfs序,求一遍dp,具体:
\(dp[i][j]\)表示dfs序前\(i\)个选\(j\)个的最大价值。
\(dp[i][j]=max(dp[i-sz[dfn[i]][j],dp[i][j-k])\)其中\(1<=k<=a_dfn[i]\)
单调队列优化后\(O(n*k)\)顺便吐槽毒瘤的数据范围
我们搞个dp2是在邻接表reverse后再跑一遍(式子同上)
最后枚举叶链的同时枚举左右两边选的大小就over了
……
我的常数很大,因为拆点,别人跑的都比我快。不拆点也可以做……
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int K=5e5+5;
const int NK=60000005;
typedef long long ll;
struct node {
int x,data;
}Q[K];
int num,n,k,sz[N],p[N],tim,dfn1[N],dfn2[N],dis[N],dp[NK][2],fa[N],a[N],val[N],Out[N];
vector<int> V[N];
inline int Id(int u,int v) {return u*(k+1)+v;}
void dfs1(int u) {
sz[u]=1;
for(int i=0;i<V[u].size();i++) {
int v=V[u][i];
dis[v]=dis[u]+val[v];
dfs1(v);
sz[u]+=sz[v];
// printf("%d %d\n",v,dis[v]);
}
dfn1[u]=++tim; p[tim]=u;
}
void dfs2(int u) {
for(int i=V[u].size()-1;i>=0;i--) {
int v=V[u][i];
dfs2(v);
}
dfn2[u]=++tim;p[tim]=u;
}
void DP(bool f) {
// for(int i=1;i<=num;i++) printf("%d ",p[i]);
// puts("");
for(int i=1;i<=num;i++) {
int h=1,t=0,u=p[i];
Q[++t]=(node){0,0};
for(int j=1;j<=k;j++) {
while(h<=t&&Q[h].x+a[u]<j) h++;
if(h<=t)dp[Id(i,j)][f]=max(dp[Id(i-sz[u],j)][f],Q[h].data+j*val[u]);
// printf("(%d,%d): %d\n",i,j,dp[Id(i,j)][f]);
node nw=(node){j,dp[Id(i-1,j)][f]-j*val[u]};
while(h<=t&&Q[t].data<=nw.data) t--;
Q[++t]=nw;
}
}
// puts("");
}
int main() {
int Q;
scanf("%d",&Q);
while(Q--) {
int ans=0;
scanf("%d%d",&n,&k);
num=n;
for(int i=1;i<=n;i++) {
scanf("%d%d%d",&fa[i],&a[i],&val[i]);
if(fa[i])V[fa[i]].push_back(i);
}
for(int i=1;i<=n;i++) {
Out[fa[i]]++;
if(a[i]>1) {
++num;V[i].push_back(num); a[num]=a[i]-1;a[i]=1;val[num]=val[i];
}
}
// printf("!\n");
tim=0;dis[1]=val[1];dfs1(1);DP(0);
// printf("!\n");
tim=0;dfs2(1);DP(1);
for(int i=1;i<=n;i++) {
if(Out[i])continue;
for(int j=0;j<=k;j++) {
// printf("%d %d %d %d %d\n",i,j,dis[i],dp[Id(dfn1[i]-1,j)][0],dp[Id(dfn2[i]-2,k-j)][1]);
ans=max(ans,dis[i]+dp[Id(dfn1[i]-1,j)][0]+dp[Id(dfn2[i]-sz[i],k-j)][1]);
}
}
printf("%d\n",ans);
for(int i=1;i<=n;i++) V[i].clear(),Out[i]=0;
memset(dp,0,sizeof(dp));
}
return 0;
}
标签:val,int,SDOI2017,tim,链上,num,苹果树,dp 来源: https://www.cnblogs.com/bestime/p/15573976.html