日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[Bzoj4182]Shopping(点分治)(树上背包)(单调队列优化多重背包)

發(fā)布時間:2023/11/29 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [Bzoj4182]Shopping(点分治)(树上背包)(单调队列优化多重背包) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

4182: Shopping


?

Time Limit:?30 Sec??Memory Limit:?128 MB
Submit:?374??Solved:?130
[Submit][Status][Discuss]

Description


?

馬上就是小苗的生日了,為了給小苗準(zhǔn)備禮物,小蔥興沖沖地來到了商店街。商店街有n個商店,并且它們之間的道路構(gòu)成了一顆樹的形狀。

第i個商店只賣第i種物品,小苗對于這種物品的喜愛度是wi,物品的價格為ci,物品的庫存是di。但是商店街有一項奇怪的規(guī)定:如果在商店u,v買了東西,并且有一個商店w在u到v的路徑上,那么必須要在商店w買東西。小蔥身上有m元錢,他想要盡量讓小苗開心,所以他希望最大化小苗對買 到物品的喜愛度之和。這種小問題對于小蔥來說當(dāng)然不在話下,但是他的身邊沒有電腦,于是他打電話給同為OI選手的你,你能幫幫他嗎?

Input


?

輸入第一行一個正整數(shù)T,表示測試數(shù)據(jù)組數(shù)。

對于每組數(shù)據(jù), 第一行兩個正整數(shù)n;m; 第二行n個非負(fù)整數(shù)w1,w2...wn; 第三行n個正整數(shù)c1,c2...cn; 第四行n個正整數(shù)d1,d2...dn; 接下來n-1行每行兩個正整數(shù)u;v表示u和v之間有一條道路

Output


?

輸出共T 行,每行一個整數(shù),表示最大的喜愛度之和。

Sample Input


?

1 3 2 1 2 3 1 1 1 1 2 1 1 2 1 3

?

Sample Output


?

4

?

HINT


?

?

?N<=500,M<=4000,T<=5,Wi<=4000,Di<=100

?

?

分析:


題意:兩個點選了,它路徑上的點必須選。求樹上一個聯(lián)通塊的多重背包,權(quán)值最大。

?

題解: 先用點分治假設(shè)重心必選,然后dfs它子樹,這樣每個點會做背包的次數(shù)降低到log次。 然后dfs子樹時列出dfs序,然后轉(zhuǎn)移方程:

?

對于第二步,多重背包優(yōu)化可以考慮二進(jìn)制拆分總復(fù)雜度為O(Tnmlognlogm)。

也可以使用單調(diào)隊列優(yōu)化總復(fù)雜度O(Tnmlogn)

下面是對單調(diào)隊列優(yōu)化的圖解:

嗯。。沒了。

AC代碼:


?

# include <cstdio> # include <cstring> # include <iostream> # include <algorithm> using namespace std; const int N = 4e3 + 12; const int M = 5e3 + 12; int mx[N],w[N],v[N],c[N],n,m,root,head[N],dt,sz[N],sum,id[N],ed[N],ans; int f[N][M]; bool vis[N]; struct Edge{int to,nex; }edge[N << 1]; void AddEdge(int u,int v) {edge[++dt] = (Edge){v,head[u]};head[u] = dt; } void find(int u,int pre) {mx[u] = 0;sz[u] = 1;for(int i = head[u];i;i = edge[i].nex){if(vis[edge[i].to] || edge[i].to == pre)continue;find(edge[i].to,u);sz[u] += sz[edge[i].to];mx[u] = max(mx[u],sz[edge[i].to]);}mx[u] = max(mx[u],sum - sz[u]);if(mx[u] < mx[root])root = u; } void dfs(int u,int pre) {sz[u] = 1;id[++dt] = u;for(int i = head[u];i;i = edge[i].nex){if(vis[edge[i].to] || edge[i].to == pre)continue;dfs(edge[i].to,u);sz[u] += sz[edge[i].to];}ed[u] = dt; } int Q1[M],Q2[M]; void calc(int *g,int x) {int h1,h2,t1,t2,cnt,t;for(int j = 0;j < v[x];j++){h1 = t1 = h2 = t2 = cnt = 0;for(int k = j;k <= m;k += v[x]){if(t1 - h1 == c[x] + 1){if(Q2[h2 + 1] == Q1[h1 + 1])++h2;++h1;}t = g[k] - cnt * w[x];Q1[++t1] = t;while(h2 < t2 && Q2[t2] < t)--t2;Q2[++t2] = t;g[k] = Q2[h2 + 1] + cnt * w[x];++cnt;}} } void solve() {for(int i = 1;i <= dt + 1;i++)for(int j = 0;j <= m;j++)f[i][j] = 0;int x,t;for(int i = dt;i >= 1;i--){x = id[i]; for(int j = m;j >= v[x];j--)f[i][j] = f[i + 1][j - v[x]] + w[x];calc(f[i],x);for(int j = m;j >= 0;j--)f[i][j] = max(f[i][j],f[ed[x] + 1][j]);}ans = max(ans,f[1][m]); } void dfs(int u) {vis[u] = true;dt = 0;dfs(u,-1);solve();for(int i = head[u];i;i = edge[i].nex){if(vis[edge[i].to])continue;root = 0;sum = sz[edge[i].to];if(sum > sz[u])sum = sum - sz[u];find(edge[i].to,u);dfs(root);} } int main() {mx[0] = N;int Case;scanf("%d",&Case);while(Case--){scanf("%d %d",&n,&m);int x,y;memset(vis,false,sizeof vis);memset(head,0,sizeof head);dt = ans = 0;for(int i = 1;i <= n;i++)scanf("%d",&w[i]);for(int i = 1;i <= n;i++)scanf("%d",&v[i]);for(int i = 1;i <= n;i++)scanf("%d",&c[i]),c[i]--;for(int i = 1;i < n;i++)scanf("%d %d",&x,&y),AddEdge(x,y),AddEdge(y,x);root = 0;sum = n;find(1,-1);dfs(root);printf("%d\n",ans);} }

?

?

?

?

轉(zhuǎn)載于:https://www.cnblogs.com/lzdhydzzh/p/8672725.html

總結(jié)

以上是生活随笔為你收集整理的[Bzoj4182]Shopping(点分治)(树上背包)(单调队列优化多重背包)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。