[樹上依賴多重揹包 DP] BZOJ 4910 [Sdoi2017]蘋果樹
題目
因為
所以可以把
這個樹上依賴揹包可以通過兩個遍歷子樹順序相反的後序遍歷和佇列優化
然後列舉每個葉子(拆點前的葉子),用兩個後序遍歷中的DP值更新答案就可以了。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=40010,K=500010;
bool bg;
int t,n,k,cnt,ncnt,DEPT,iG[N],a[N],v[N],lef[N],dpt[N],sz[N],son[N];
int f[51000010],g[51000010];
int val[N];
ll tot;
struct edge{
int t,nx;
}E[N];
int Ap[N],Bp[N],pa[N],pb[N],A[N],B[N],At,Bt;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void rea(int &x){
char c=nc(); x=0;
for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}
inline void adde(int x,int y){
E[++cnt].t=y; E[cnt].nx=iG[x]; iG[x]=cnt;
}
void dfsf(int x){
Ap[x]=At; sz[x]=1;
for(int i=iG[x];i;i=E[i].nx){
val[E[i].t]=val[x]+v[E[i].t];
dpt[E[i].t]=dpt[x]+1;
dfsf(E[i].t),sz[x]+=sz[E[i].t];
}
A[++At]=x; pa[x]=At;
}
void dfsb(int x){
vector<int> son; Bp[x]=Bt;
for(int i=iG[x];i;i=E[i].nx) son.push_back(E[i].t);
for(int i=son.size()-1;~i;i--) dfsb(son[i]);
B[++Bt]=x; pb[x]=Bt;
}
#define F(x,y) f[(x)*(k+1)+(y)]
#define G(x,y) g[(x)*(k+1)+(y)]
//ll &F(int x,int y){ return f[(x)*(k+1)+(y)]; }
//ll &G(int x,int y){ return g[(x)*(k+1)+(y)]; }
int Q[K],Q1[K],qh,qt;
bool ed;
inline void DP(int *A,int *p,int *f){
for(int i=1;i<=ncnt;i++){
int x=A[i];
int *ff=f+(i-1)*(k+1),*fc=f+i*(k+1);
memcpy(fc,f+p[x]*(k+1),sizeof(int)*(k+1));
//for(int j=0;j<=k;j++) fc[j]=F(p[x],j);
if(a[x]==0) continue;
if(a[x]==1){
for(int j=1;j<=k;j++)
fc[j]=max(fc[j],ff[j-1]+v[x]);
continue;
}
qh=qt=1;
Q[qh]=0; Q1[qh]=0;
for(int j=1;j<=k;j++){
fc[j]=max(fc[j],j*v[x]+Q1[qh]);
while(qh<=qt && Q[qh]<=j-a[x]) qh++;
int cur=ff[j]-j*v[x];
while(qh<=qt && Q1[qt]<cur) qt--;
Q[++qt]=j; Q1[qt]=cur;
}
}
}
int ans;
int main(){
rea(t);
while(t--){
rea(n); rea(k);
for(int i=1;i<=n;i++) iG[i]=lef[i]=val[i]=son[i]=0;
//memset(f,0,sizeof(f));
//memset(g,0,sizeof(g));
cnt=0; ncnt=n; At=Bt=0; ans=0; tot=0;
for(int i=1;i<=n;i++){
int p; rea(p); rea(a[i]); rea(v[i]); tot+=a[i];
if(p) adde(p,i),lef[p]=1;
if(a[i]>1)
a[son[i]=++ncnt]=a[i]-1,v[ncnt]=v[i],a[i]=1,adde(i,ncnt),lef[ncnt]=1;
}
val[1]=v[1]; dpt[1]=1;
dfsf(1); dfsb(1);
DP(A,Ap,f);
DP(B,Bp,g);
//printf("%d\n",clock()-ttt);
/*for(int i=1;i<=n;i++)
for(int j=0;j<=k;j++)
cout<<i<<' '<<j<<' '<<F(pa[i],j)<<' '<<G(pb[i],j)<<endl;*/
for(int i=1;i<=n;i++)
if(!lef[i]){
int cur=min((ll)k,tot-dpt[i]);
int *ff=f+(pa[i]-1)*(k+1),*gg=g+(pb[i]-sz[i])*(k+1);
for(int j=0;j<=cur;j++)
ans=max(ans,ff[j]+gg[cur-j]+val[i]);
}
//printf("%d\n",clock()-ttt);
printf("%d\n",ans);
}
//printf("%d\n",(&ed-&bg)/1024/1024);
return 0;
}
相關推薦
[樹上依賴多重揹包 DP] BZOJ 4910 [Sdoi2017]蘋果樹
題目 t−h≤k 的限制其實就是選一條到葉節點的鏈,然後再選k個的最大值(因為vi都大於零)。 因為 ai>1 的點,肯定是先選了第一個才會選第二個 所以可以把 ai>1 的點拆成兩個點 i′,i′′,ai′=1,ai′′=ai−1,讓 i′′
[樹形依賴多重揹包] BZOJ 4910 [Sdoi2017] 蘋果樹
首先解決依賴揹包 如果是0/1揹包,按照後序遍歷dp,根據選或不選決策 現在是多重揹包,那麼這個點只留一個,剩下的變成一個新點掛上去,這樣仍然滿足依賴關係, 轉移的時候多重揹包用單調對列優化 可以發現如果除了最長的一條鏈,剩餘最多K個 因為權值為正 那
HDU-2191-悼念512汶川大地震遇難同胞——珍惜現在,感恩生活(多重揹包+dp)
急!災區的食物依然短缺! 為了挽救災區同胞的生命,心繫災區同胞的你準備自己採購一些糧食支援災區,現在假設你一共有資金n元,而市場有m種大米,每種大米都是袋裝產品,其價格不等,並且只能整袋購買。 請問:你用有限的資金最多能採購多少公斤糧食呢? 後記: 人生是一個充滿了變數的生命過程,天災、人禍、病痛是
【bzoj1531】[POI2005]Bank notes 多重揹包dp
演算法:單調佇列/二進位制 優化多重揹包 難度:NOIP 首先,他是一個裸的多重揹包,所以我們來貼一個暴力程式碼吧,時間複雜度O(n*v*val) 程式碼如下: #include <cst
JZOJ4202. Shopping(點分治+樹形依賴+多重揹包)
題意: 一顆樹,每個點代表一個物品,空間c[i]c[i],數量d[i]d[i],價值w[i]w[i],現有一個空間為mm的揹包,選樹上相互連線的物品,求最大價值 想法: 一眼樹形揹包,時間複雜度上天 f[i][j]f[i][j]表示i的子樹內,i
01揹包 ,完全揹包,多重揹包 dp (動態規劃入門dp)
dp 動態規劃,確實難啃, 光 最簡單的 揹包問題,就 費老大勁. 思想! 思想! 思想! 類似於遞推, 區域性找 關係. 揹包問題, 就兩種狀態 放還是不放? 其實關於揹包放不放的
BZOJ.4910.蘋果樹(樹形依賴揹包 DP 單調佇列)
BZOJ 洛谷 \(shadowice\)已經把他的思路說的很清楚了,可以先看一下會更好理解? 這篇主要是對\(Claris\)題解的簡單說明。與\(shadowice\)的做法還是有差異的(比如並沒有明顯用到後序遍歷的性質),而且用這種寫法可能跑的比較輕鬆? 問題等價於樹形依賴揹包,允許一條鏈每個
BZOJ.3425.[POI2013]Polarization(DP 多重揹包)
BZOJ 洛谷 最小可到達點對數自然是把一條路徑上的邊不斷反向,也就是黑白染色後都由黑點指向白點。這樣答案就是\(n-1\)。 最大可到達點對數,容易想到找一個點\(a\),然後將其子樹分為兩部分\(x,y\),\(x\)子樹所有邊全指向\(a\),\(a\)與\(y\)子樹之間的邊全指向\(y\)。這樣答
BZOJ 4182 Shopping (樹分治+樹上多重揹包)
題目大意:給你一顆樹,你有$m$元錢,每個節點都有一種物品,價值為$w$,代價為$c$,有$d$個,如果在$u$和$v$兩個城市都購買了至少一個物品,那麼$u,v$路徑上每個節點也都必須買至少一個物品 單調佇列陣列開小了調了2h 通過這道題,本蒟蒻終於$get$到了樹上帶權揹包的正確姿勢 合併揹包的代價
2018.10.18每天認真做一道數學(數論)題之BZOJ 1042 [HAOI2008] 硬幣購物【揹包DP】【容斥原理】
對於每個詢問,答案顯然為:S所有超過數量限制的方案數- c [ 1
[ NAIPC2016 ] D Programming Team [01分數規劃 + 樹上依賴揹包]
UpCoder is looking to assign their best employees to a team tasked with designing their new and improved website, and they’re looking to you to
HDU-2844 Coins 【動態規劃DP+多重揹包】
題目傳送門 題目:有n種硬幣,第i種硬幣的價值為Ai,數目為Ci,求這些硬幣能配出1~m中的幾種價值。 題解:dp[j]表示是否能配出價值j。sum[i][j]表示第i種硬幣取到價值j時需要的數目。sum陣列可以壓掉i的那一維,每次都要記得清零。 AC程式碼: #include
POJ-2392 Space Elevator 【動態規劃DP+多重揹包】
題目傳送門 題目:牛要去太空了!他們計劃通過建造一種太空升降機來達到軌道:一個巨大的積木塔。他們有K (1 <= K <= 400)不同型別的積木來建造塔。型別i的每個塊的高度都是h_i (1 <= h_i <= 100),並且數量上都是c_i (1 <= c_
POJ-1276-Cash Machine(DP+多重揹包問題)
A Bank plans to install a machine for cash withdrawal. The machine is able to deliver appropriate @ bills for a requested cash amount. The machine use
LintCode 799: Backpack VIII (多重揹包問題變種,DP經典題, 難!)
我開始覺得這就是多重揹包問題的變種,但是解法1會超時。因為有3重迴圈,所以當amount[i]的值都很大就超時了。 解法1: dp[i]表示coins是否可以組合成i的值。 class Solution { public: /** * @param n: the
BZOJ P1296 [SCOI2009]粉刷匠【區間DP】【揹包DP】
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #defin
dp (多重揹包的二進位制優化)
在多重揹包的問題中,有時物品的件數會給的非常大,此時從1件到n件遍歷很容易超時,下面講一下二進位制優化的思路 答題思想為:把同種多件物體轉換為多種單件物體。 我們已經知道,1、2、4、8 、16 、 32……2^n 可以組成從1到2^(n+1)-1中的任何數
樹上依賴揹包總結
做了很多關於樹上dp的題有些使用了nm^2的演算法來進行dp,可是當m過大時方法不大試用,又可以對這種演算法進行優化,如dfs序或者直接樹上合併揹包。當輸出方案時又需要多叉轉二叉進行dp尋找方案,很多方法於是乎總結一下。 w[i]表示第i個節點的重量,v[i]表示第i個節點的價值,f[i][j]表示以i
BZOJ 1618 [Usaco2008 Nov] Buying Hay 購買乾草【揹包DP】
其實就是一個完全揹包。 問題在於至少要買HHH磅乾草。 然後我們發現出售的乾草最多500050005000磅,所以只需要在做完全揹包的時候擴大一下揹包的範圍然後結果取最小即可。 #include <cmath> #include <cstdio
BZOJ 1334 [Baltic2008]Elect【揹包DP】
這是一道01揹包。 我們先來翻譯題目限制: 我們選擇的人數總數>原來的人數總數/2 我們選擇的人數總數-我們選擇的人數最少的政黨的人數≤\leq≤原來的人數總數/2 所以我們考慮將人數從大到小排序,然後正常做01揹包,中間判斷一下最少的政黨的人數是