<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      樹上依賴性背包 學習筆記 | P6326 Shopping 題解

      樹上依賴性背包

      樹上依賴性背包,適用于合并復雜度大,插入復雜度小的情況。


      P6326 Shopping

      發現題意等價于在樹上選一個連通塊。

      完全背包先二進制拆分,然后想出一車假做法。思考正解。直接寫 Solution 吧。

      #1:點分治 + dfn 序

      百家博大學習。WC 好像會了。

      一個常見的技巧是,我們樹上背包是劣于序列背包的,所以對于這類問題,可以想辦法把它拍成序列上的問題。注意到一個根以及它的子樹節點的時間戳在 dfn 序列上一定是連續的一段,可以由此入手。

      考慮先隨便欽定一個根,并且欽定在根至少選 \(1\) 個物品。

      然后我們搞出 dfn 序把樹拍成序列。由于 dfn 序列上后面的點不可能是前面的點的祖先,考慮倒序設計 dp 簡化問題。

      \(siz[i]\) 為以 \(i\) 為根子樹大小。

      \(dp[i][j]\) 為考慮 dfn 序為 \([i,n]\) 的節點,一共花費為 \(j\),所能得到的最大價值。

      轉移分為兩種情況:

      • 不選以 \(i\) 為根子樹,包括 \(i\)。那么令 \(dp[i][j]=dp[i+siz_i][j]\) 即可。含義為直接繼承這個子樹之外 dfn 序上下一個節點的 dp 值。

      • 選以 \(i\) 為根子樹,即必須保證 \(i\) 至少選 \(1\) 個。那么 \(dp[i][j]\) 直接可以由 \(dp[i+1][k]\)\(i\) 這個物品做多重背包直接轉移即可。

      最后答案對 \(dp[1][i]\)\(\max\) 即可。因為根的 dfn 序為 \(1\)

      這樣做一次 dp 復雜度是 \(O(nm \log m)\) 的。我們還需要對于每個點都欽定為根跑 dp,總復雜度 \(O(n^2m\log m)\) 的。已經比樹上直接做是 \(O(nm^2 \log m)\) 優了,但還不夠。

      發現選的是樹上連通塊。那么可以考慮直接點分治做,正確性可以保證。

      時間復雜度 \(O(n\log m \log m)=O(nm\log n \log m)\),可以通過。注意本題多測。

      Code:

      WC 了,我代碼犯的全是唐詩錯誤,包括 01 背包正著枚舉,\(dp[i+siz_{dfn[i]]}\) 寫成 \(dp[i+siz_i]\)

      #include<bits/stdc++.h>
      #define int long long
      
      using namespace std;
      
      const int Size=(1<<20)+1;
      char buf[Size],*p1=buf,*p2=buf;
      char buffer[Size];
      int op1=-1;
      const int op2=Size-1;
      #define getchar()                                                              \
      (tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
      	? EOF                                                                 \
      	: *ss++)
      char In[1<<20],*ss=In,*tt=In;
      inline int read()
      {
      	int x=0,c=getchar(),f=0;
      	for(;c>'9'||c<'0';f=c=='-',c=getchar());
      	for(;c>='0'&&c<='9';c=getchar())
      		x=(x<<1)+(x<<3)+(c^48);
      	return f?-x:x;
      }
      inline void write(int x)
      {
      	if(x<0) x=-x,putchar('-');
      	if(x>9)  write(x/10);
      	putchar(x%10+'0');
      }
      
      const int N=505;
      vector<int> v[N],E[N];
      int n,m;
      int w[N];
      int c[N];
      int d[N];
      
      void chai(int id,int x)
      {
          if(!x) return;
          v[id].clear();
          for(int k=0;;k++)
          {
              int nw=1ll<<k;
              if(nw>x)
              {
                  nw>>=1;
                  v[id].pop_back();
                  v[id].push_back(x-nw+1);
                  return;
              }
              v[id].push_back(nw);
          }
      }
      
      bool vis[N];
      int siz[N];
      pair<int,int> findG(int p,int fa,int tot)
      {
          pair<int,int> nw=make_pair(tot-siz[p],p),ans=make_pair(n+1,-1);
          for(int to:E[p])
          {
              if(to==fa) continue;
              if(vis[to]) continue;
              ans=min(ans,findG(to,p,tot));
              nw.first=max(nw.first,siz[to]);
          }
          return min(ans,nw);
      }
      
      void dfs(int p,int fa)
      {
          siz[p]=1;
          for(int to:E[p])
          {
              if(to==fa) continue;
              if(vis[to]) continue;
              dfs(to,p);
              siz[p]+=siz[to];
          }
      }
      
      int tot;
      int id[N];
      int dp[N][4005];
      void dfs1(int p,int fa)
      {
          siz[p]=1;
          id[++tot]=p;
          for(int to:E[p])
          {
              if(to==fa) continue;
              if(vis[to]) continue;
              dfs1(to,p);
              siz[p]+=siz[to];
          }
      }
      
      int ans=0;
      
      void dodp(int root)
      {
          int nw=ans;
          tot=0;
          dfs1(root,0);
          for(int i=1;i<=tot+1;i++) memset(dp[i],0,sizeof(dp[i]));
      
          for(int i=tot;i>=1;i--)
          {
              int p=id[i];
              for(int j=c[p];j<=m;j++) dp[i][j]=dp[i+1][j-c[p]]+w[p];
              for(int cnt:v[p])
              {
                  int V=cnt*c[p],W=cnt*w[p];
                  for(int j=m;j>=V;j--)
                      dp[i][j]=max(dp[i][j],dp[i][j-V]+W);
              }
      
              for(int j=1;j<=m;j++) dp[i][j]=max(dp[i+siz[p]][j],dp[i][j]);
          }
          for(int i=1;i<=m;i++) ans=max(ans,dp[1][i]);
      }
      
      void solve(int root)
      {
          vis[root]=1;
          for(int to:E[root])
          {
              if(vis[to]) continue;
              dfs(to,0);
              int nxt=findG(to,0,siz[to]).second;
              solve(nxt);
          }
          dodp(root);
          vis[root]=0;
      }
      
      void solve()
      {
          n=read();
          m=read();
          for(int i=1;i<=n;i++) w[i]=read();
          for(int i=1;i<=n;i++) c[i]=read();
          for(int i=1;i<=n;i++) d[i]=read();
          for(int i=1;i<=n;i++) chai(i,d[i]-1),E[i].clear();
          for(int i=1;i<n;i++)
          {
              int u=read(),v=read();
              E[u].push_back(v);
              E[v].push_back(u);
          }
      
          dfs(1,0);
          int root=findG(1,0,siz[1]).second;
          solve(root);
      
          cout<<ans<<"\n";
          ans=0;
      }
      
      
      signed main()
      {
          int T=read();
          while(T--) solve();
      	return 0;
      }
      
      

      #2:樹上啟發式合并。

      (Waiting)


      AI 潤色

      AI 潤色之后 # 樹上依賴性背包

      適用于合并操作復雜度較高,而插入操作復雜度較低的場景。


      P6326 Shopping

      在分析題目時,我們發現題目的本質等價于在樹中選取一個連通塊

      首先,將完全背包問題利用二進制拆分處理,再探索多種簡化策略,最終確定了有效的解決方案。下面介紹一種基于動態規劃(DP)的思路。

      方法一:點分治 + dfn 序列

      參考百家博等人的思路,主要步驟如下:

      1. 問題轉化:樹上背包問題通常不如序列背包問題高效,因此可以嘗試將樹轉化為序列。注意,一個節點及其子樹在 DFS 得到的 dfn 序列中通常是連續的。
      2. 選取根節點:任選一個根節點,并保證該根至少選擇一個物品。
      3. 構造 dfn 序列并逆序 DP:利用 DFS 得到的 dfn 序列,由于序列后面的節點不可能是前面節點的祖先,可以采用逆序 DP 來進行狀態轉移。

      設定如下變量:

      • \(siz[i]\):以 \(i\) 為根的子樹大小。
      • \(dp[i][j]\):在 dfn 序列中,從第 \(i\) 個節點開始,當總花費不超過 \(j\) 時所能獲得的最大價值。

      狀態轉移分析:

      • 不選方案:不選取以 \(i\) 為根的子樹,此時有 \(dp[i][j] = dp[i+siz[i]][j]\)。
      • 選取方案:選擇以 \(i\) 為根的子樹,必須至少選中一個物品,通過多重背包思想更新 \(dp[i][j]\)。

      最終答案由 \(dp[1][j]\) 的最大值確定,其中索引 1 對應樹的根。

      該方法單次 DP 的時間復雜度為 \(O(nm\log m)\),但由于需要對每個節點作為根進行處理,總體復雜度為 \(O(n^2m\log m)\)。相比直接在樹上求解(\(O(nm^2\log m)\))已經有所提升,但仍有優化空間。

      進一步地,由于題目要求選取的是樹上連通塊,可以直接采用點分治策略將時間復雜度降低至 \(O(nm\log n\log m)\),從而滿足較大數據規模的要求(注意測試數據較多)。

      示例代碼

      在編碼過程中,常見錯誤包括:

      • 01 背包中正向枚舉導致錯誤;
      • 狀態轉移時容易將 \(dp[i+siz[dfn[i]]\) 錯寫為 \(dp[i+siz[i]]\)

      以下為主要代碼實現:

      #include<bits/stdc++.h>
      #define int long long
      
      using namespace std;
      
      const int Size = (1<<20) + 1;
      char buf[Size], *p1 = buf, *p2 = buf;
      char buffer[Size];
      int op1 = -1;
      const int op2 = Size - 1;
      #define getchar() (tt == ss && (tt=(ss=In)+fread(In, 1, 1<<20, stdin), ss == tt) ? EOF : *ss++)
      char In[1<<20], *ss = In, *tt = In;
      
      inline int read() {
          int x = 0, c = getchar(), f = 0;
          for(; c > '9' || c < '0'; f = (c=='-'), c = getchar());
          for(; c >= '0' && c <= '9'; c = getchar())
              x = (x << 1) + (x << 3) + (c ^ 48);
          return f ? -x : x;
      }
      
      inline void write(int x) {
          if(x < 0) x = -x, putchar('-');
          if(x > 9) write(x/10);
          putchar(x % 10 + '0');
      }
      
      const int N = 505;
      vector<int> v[N], E[N];
      int n, m;
      int w[N], c[N], d[N];
      
      void chai(int id, int x) {
          if(!x) return;
          v[id].clear();
          for(int k = 0;; k++) {
              int nw = 1ll << k;
              if(nw > x) {
                  nw >>= 1;
                  v[id].pop_back();
                  v[id].push_back(x - nw + 1);
                  return;
              }
              v[id].push_back(nw);
          }
      }
      
      bool vis[N];
      int siz[N];
      
      pair<int,int> findG(int p, int fa, int tot) {
          pair<int,int> nw = make_pair(tot - siz[p], p), ans = make_pair(n+1, -1);
          for(int to : E[p]) {
              if(to == fa || vis[to]) continue;
              ans = min(ans, findG(to, p, tot));
              nw.first = max(nw.first, siz[to]);
          }
          return min(ans, nw);
      }
      
      void dfs(int p, int fa) {
          siz[p] = 1;
          for(int to : E[p]) {
              if(to == fa || vis[to]) continue;
              dfs(to, p);
              siz[p] += siz[to];
          }
      }
      
      int tot, id[N];
      int dp[N][4005];
      
      void dfs1(int p, int fa) {
          siz[p] = 1;
          id[++tot] = p;
          for(int to : E[p]) {
              if(to == fa || vis[to]) continue;
              dfs1(to, p);
              siz[p] += siz[to];
          }
      }
      
      int ans = 0;
      
      void dodp(int root) {
          tot = 0;
          dfs1(root, 0);
          for(int i = 1; i <= tot + 1; i++) memset(dp[i], 0, sizeof(dp[i]));
          for(int i = tot; i >= 1; i--) {
              int p = id[i];
              for(int j = c[p]; j <= m; j++)
                  dp[i][j] = dp[i + 1][j - c[p]] + w[p];
              for(int cnt : v[p]) {
                  int V = cnt * c[p], W = cnt * w[p];
                  for(int j = m; j >= V; j--)
                      dp[i][j] = max(dp[i][j], dp[i][j - V] + W);
              }
              for(int j = 1; j <= m; j++)
                  dp[i][j] = max(dp[i][j], dp[i + siz[p]][j]);
          }
          for(int i = 1; i <= m; i++) ans = max(ans, dp[1][i]);
      }
      
      void solve(int root) {
          vis[root] = true;
          for(int to : E[root]) {
              if(vis[to]) continue;
              dfs(to, 0);
              int nxt = findG(to, 0, siz[to]).second;
              solve(nxt);
          }
          dodp(root);
          vis[root] = false;
      }
      
      void solve() {
          n = read();
          m = read();
          for(int i = 1; i <= n; i++) w[i] = read();
          for(int i = 1; i <= n; i++) c[i] = read();
          for(int i = 1; i <= n; i++) d[i] = read();
          for(int i = 1; i <= n; i++) {
              chai(i, d[i] - 1);
              E[i].clear();
          }
          for(int i = 1; i < n; i++) {
              int u = read(), v = read();
              E[u].push_back(v);
              E[v].push_back(u);
          }
          dfs(1, 0);
          int root = findG(1, 0, siz[1]).second;
          solve(root);
          cout << ans << "\n";
          ans = 0;
      }
      
      signed main() {
          int T = read();
          while(T--) solve();
          return 0;
      }
      

      方法二:樹上啟發式合并

      (內容待更新)

      posted @ 2025-10-21 19:03  Wy_x  閱讀(19)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 色播久久人人爽人人爽人人片av| 日韩精品久久久肉伦网站| 国产精品视频第一第二区| 韩国三级+mp4| 99视频在线精品国自产拍| 亚洲毛片不卡AV在线播放一区 | 亚洲国产精品无码久久电影| 人妻系列无码专区69影院| 综合久久av一区二区三区| 高清中文字幕国产精品| 日本一本正道综合久久dvd| 东兰县| 91热在线精品国产一区| 久久一级精品久熟女人妻| 国产午精品午夜福利757视频播放| 在线 | 国产精品99传媒a| 精品久久久久久中文字幕202| 国产精品熟女一区二区不卡| 顶级少妇做爰视频在线观看| 国产乱啊有帅gv小太正| 国产亚洲精品成人aa片新蒲金| 午夜国产理论大片高清| 99精品全国免费观看视频 | 日韩精品在线观看一二区| 亚洲 欧洲 自拍 偷拍 首页| 好爽毛片一区二区三区四| 无码专区视频精品老司机| 久久夜色精品国产亚洲a| 中文字幕无码免费久久9一区9 | 久久夜色精品国产亚洲av| 日韩精品一区二区三区中文无码 | 国产精品嫩草99av在线| 国产精品偷乱一区二区三区 | 贵州省| 极品少妇的粉嫩小泬看片| 精品国产亚洲午夜精品a| a4yy私人毛片| 国产亚洲久久久久久久| 亚洲最大天堂在线看视频| 日韩大片在线永久免费观看网站| 亚洲欧洲精品日韩av|