[九省聯考2018]秘密襲擊coat
阿新 • • 發佈:2018-09-13
背包 我們 code max oot () val ont 狀態
樹形dp碾壓標算
這道題目我寫了60分的暴力(N^2*k),沒有優化,只是說一說樹形dp相關。
這道題我們可以轉化一下,我們可以考慮每一個點對答案的貢獻。這道題可以轉化為以該點為根的樹中包含根的連通塊,且其中有k個點大於等於根的危險度的方案數。這裏“等於”有一定的問題,就是同一種連通塊可能會被其他的根重復統計,所以要特判一下,dan[s]==pan&&s<root 才認為其合法。
狀態轉移是O(k)的,具體看代碼;
code:
#include<iostream> #include<cstdio> #include<queue> #include<algorithm> #include<cstring> #include<cmath> using namespace std; const int maxn=2006; const int mod=64123; vector<int>e[maxn]; int n,k,w,dan[maxn]; int val[maxn],size[maxn]; int f[maxn][maxn]; inline void dfs(int s,int fa,int pan,int root) { if (dan[s]>pan||(dan[s]==pan&&s<root)) { for (int i=1;i<k;++i) { f[s][i+1]=f[fa][i]; if (f[s][i+1]>mod) f[s][i+1]-=mod; } } else for (int i=1;i<=k;++i) {f[s][i]=f[fa][i];if (f[s][i]>mod) f[s][i]-=mod;} for (int i=0;i<e[s].size();++i) { int v=e[s][i]; if (v==fa) continue; dfs(v,s,pan,root); } for (int i=1;i<=k;++i) {f[fa][i]+=f[s][i];if (f[fa][i]>mod) f[fa][i]-=mod;} } signed main() { cin>>n>>k>>w; for (int i=1;i<=n;++i) { scanf("%d",&dan[i]); } for (int i=1,a,b;i<=n-1;++i) { scanf("%d%d",&a,&b); e[a].push_back(b); e[b].push_back(a); } long long ans=0; for (int i=1;i<=n;++i) { memset(f,0,sizeof(f)); f[i][1]=1; for (int j=0;j<e[i].size();++j) { dfs(e[i][j],i,dan[i],i); } ans=ans+f[i][k]%mod*dan[i]%mod; ans%=mod; } cout<<ans; return 0; }
收獲:計數類樹形依賴背包dp問題,可以先強制轉移下來,這麽一直轉移到葉節點,最後回溯時加上兒子節點完善好的dp值。
[九省聯考2018]秘密襲擊coat