P9111 [福建省队集训2019] 最大权独立集问题
题意
给定一颗 n 点的树,点有权值 di。
有操作:
- 选择一个点 u,对于该点所有连边且之前未被选择过的点 v,使得 dv←dv+du,然后该点删除,对答案贡献 du。
确定选择顺序,求 max∑i=1ndu。
注意:对于每次操作 di 都继承新的权值 di+x。其实就是不区分 di 和 di′,即 di 每次都是最新的值。
数据范围:保证 ∣di∣≤109,1≤ci<i,1≤n≤400。
思路
首先若一个点被选择后,那么他的贡献会具有传递性,传递给之后所有还未被选择的点。
那么设 tu 为点 u 被选择的时间,若 tu<tv 我们连 u→v 即代表着权值 du 流向 dv,注意这里的 du 可能也继承着别的点流向 u 的 di。
此题因此转化为给每条边定向求得最大权值。
那我们考虑拆贡献,分别考虑每个 di 对答案的贡献的数量,那么答案即为 ans=∑i=1ndi×cnti,d 为点权,cnt 为点权的贡献次数。
那我们考虑树形 dp 求得答案。
设 fu,i,j 代表已经考虑完点 u 子树内的贡献,能到的点有 i 个,其中在其子树(包含自己)内的点有 j 个。
请理解这里的能到并非单指连向的边,而是能通过有向边传递到的点的个数。这也正是区别于其他树形 dp 特殊的地方。
初始 fu,i,1=du,请注意这里的 i−1 个额外贡献还未算入其内,我们在转移的时候更新。
那么我们考虑 u 与 v 的边的方向。
若 u→v,那么 fu,i,j+k⟵maxfu,i,j+fv,k,k+k×du。
对于该种情况我们发现点 v 的贡献只能流向它的子树中,不会有额外的贡献,因此我们分析得知我们每次定向都会影响点 v 的流量,因此我们要枚举点 v 的指出数量 k,将贡献加入其中,即 fv,k,k+k×du。
这个图 v 点不一定都是指出啊,也有可能是 v 的儿子指入。
若 u←v,那么 fu,i,j⟵maxfu,i,j+fv,i+k,k。
这种情况同理,图就不画了。这种情况下,v 的贡献也会同时向外流出 i×dv,因此是 fv,i+k,k。
fu,i,j=fu,i,j+(i−j)×du 这里就是类似于费用提前计算的思想将额外 (i−j)×du 的贡献加入其内。
强调:
-
注意转移不能有后效性,必须得从上一个的去更新,因此就是得临时存一下 fu,i,j 放到 tmpi,j。
-
还有对于 f 要初始化为极小值,因为 ∣di∣≤109。
时间复杂度为 O(n3) 易分析。
CODE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #include<iostream> #include<vector> #define int long long using namespace std; template<typename T>inline void read(T &x){ x=0;T f=1;char ch=getchar(); while(ch<48||ch>57){if(ch=='-'){f=-1;}ch=getchar();} while(ch>=48&&ch<=57){x=x*10+ch-48;ch=getchar();} x*=f; } template<typename T>inline void write(T x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+48); } const int N=408; int d[N],c[N]; int n,ans; vector<int> G[N]; int f[N][N][N]; int sz[N]; int tmp[N][N]; void dfs(int u,int fa){ sz[u]=1; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ f[u][i][j]=-1e18; } } for(int i=1;i<=n;i++){ f[u][i][1]=d[u]; } for(auto v:G[u]){ if(v==fa) continue; dfs(v,u); for(int i=1;i<=n;i++){ for(int j=1;j<=sz[u]&&j<=i;j++){ tmp[i][j]=f[u][i][j]; f[u][i][j]=-1e18; } } for(int i=1;i<=n;i++){ for(int j=1;j<=sz[u]&&j<=i;j++){ for(int k=1;k<=sz[v]&&j+k<=i;k++){ f[u][i][j+k]=max(f[u][i][j+k],tmp[i][j]+f[v][k][k]+k*d[u]); } for(int k=1;k<=sz[v];k++){ f[u][i][j]=max(f[u][i][j],tmp[i][j]+f[v][i+k][k]); } } } sz[u]+=sz[v]; } for(int i=1;i<=n;i++){ for(int j=1;j<=sz[u]&&j<=i;j++){ f[u][i][j]+=d[u]*(i-j); } }
signed main(){ read(n); for(int i=1;i<=n;i++){ read(d[i]); } for(int i=2;i<=n;i++){ read(c[i]); G[c[i]].push_back(i); G[i].push_back(c[i]); } dfs(1,0); ans=-1e18; for(int i=1;i<=n;i++){ ans=max(ans,f[1][i][i]); } write(ans); return 0; }
|
__END__