CF1293E-Xenon's Attack on the Gangs 树状DP

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 }

 

posted @ 2020-02-10 10:49  IAT14  阅读(119)  评论(0编辑  收藏  举报