0边把图分成两个部分,这两个部分的路径之间,mex起码为1,都对答案产生1的贡献。
然后1边接在0边旁边,把图分成了更小的两个部分(0,1的两端),和一些不会再产生新的贡献的区域,这两个更小的部分路径之间,mex起码为2,都对答案又产生了1的贡献。(他们在刚刚算mex起码为1的时候,已经贡献过1了,所以再贡献1,mex就起码为2了)。
依次类推,我们发现最后只有一条链上的边放置的是有意义的,其他边都可以随便放,反正不会产生贡献。
我们考虑这种情况下如何求解。
如果只考虑一条链,dp[i][j]从i边开始到j边 结束,放置0~(j-i)这些边的最大贡献。dp[i][j] = lcnt[i] * rcnt[j] + max(dp[i + 1][j],dp[i][j - 1])。
这道题允许O(N^2)做法,所以我们可以枚举每条链,然后用这个DP就行了。
siz[i][root]以root为根,i的孩子有多少。fa[i][root]以root为根,i的父亲是谁。
dp[x][y]就是把x和y之间的路径放置的最大贡献。和上面转移是一样的,只不过看 x的时候,就把y当根,来使得所计算的贡献都是正确的。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 typedef long long ll; 6 const int MAXN = 3100; 7 int cnt,n; 8 int head[MAXN],siz[MAXN][MAXN],fa[MAXN][MAXN],nxt[2 * MAXN],to[2 * MAXN]; 9 ll dp[MAXN][MAXN],ans; 10 void add(int x,int y) 11 { 12 nxt[++cnt] = head[x]; 13 to[cnt] = y; 14 head[x] = cnt; 15 } 16 void dfs(int x,int root) 17 { 18 siz[x][root] = 1; 19 for (int i = head[x];i;i = nxt[i]) 20 { 21 if (to[i] == fa[x][root]) 22 continue; 23 fa[to[i]][root] = x; 24 dfs(to[i],root); 25 siz[x][root] += siz[to[i]][root]; 26 } 27 } 28 ll dfs2(int x,int y) 29 { 30 if (dp[x][y] != -1) 31 return dp[x][y]; 32 return dp[x][y] = siz[x][y] * siz[y][x] + max(dfs2(x,fa[y][x]),dfs2(fa[x][y],y)); 33 } 34 int main() 35 { 36 scanf("%d",&n); 37 int tu,tv; 38 for (int i = 1;i <= n - 1;i++) 39 { 40 scanf("%d%d",&tu,&tv); 41 add(tu,tv); 42 add(tv,tu); 43 } 44 memset(dp,-1,sizeof(dp)); 45 for (int i = 1;i <= n;i++) 46 dp[i][i] = 0; 47 for (int i = 1;i <= n;i++) 48 dfs(i,i); 49 for (int i = 1;i <= n;i++) 50 for (int j = 1;j <= n;j++) 51 ans = max(ans,dfs2(i,j)); 52 printf("%lld\n",ans); 53 return 0; 54 }
CF1293E-Xenon's Attack on the Gangs 树状DP
原文地址:https://www.cnblogs.com/iat14/p/12290105.html
时间: 2024-10-12 13:59:31