最近在做些树形DP练练手

原题链接

大意就是给你一棵树,你可以断开任意数量的边,使得剩下的联通块大小乘积最大。

样例

8

1 2

1 3

2 4

2 5

3 6

3 7

6 8

输出

18

我首先想的是设\(f[i]\)表示以\(i\)为根的子树可获得的最大收益,但是会发现这样无法转移。考虑再加一维,\(f[i][j]\)表示以\(i\)的子树中,\(i\)所在的联通块大小为\(j\)的最大价值。然后我就傻了,想了半天也没想起来怎么转移,最后只好看了一眼题解。其实转移好简单的,貌似是个树上背包?考虑在\(dfs\)的过程中进行\(DP\),每当访问完一个点\(i\)的子结点时,累加一下\(sz[i]\),就枚举\(j\),并且用当前子结点的\(DP\)值来更新\(f[i][j]\)。转移方程大概会长成下面这个样子:

$f[i][j]=max(f[i][j],f[i][k]*f[v][j-k])$
(理解的话,就是把之前的大小为$k$的联通块和在当前子树中大小为$j-k$的联通块拼起来)
同时,我们特别定义$f[i][0]$表示以$i$为根的子树可获得的价值,则他的转移方程比较特殊:
$f[i][0]=max(f[i][0],f[i][j]*j)$
如果到这里这道题就结束的话,代码会长成下面这样:
```cpp
#include

using namespace std;

define N 700

define ll long long

int n, eid, sz[N+5], head[N+5];

ll f[N+5][N+5];

struct Edge {

int next, to;

}e[2*N+5];

void addEdge(int u, int v) {

e[++eid].next = head[u];

e[eid].to = v;

head[u] = eid;

}

void dp(int u, int fa) {

sz[u] = 1, f[u][0] = f[u][1] = 1;

for(int i = head[u]; i; i = e[i].next) {

int v = e[i].to;

if(v == fa) continue;

dp(v, u);

sz[u] += sz[v];

for(int j = sz[u]; j >= 1; --j) { //枚举i所在的联通块大小

for(int k = min(j, sz[u]-sz[v]); k >= max(1, j-sz[v]); --k) { //枚举子树根结点所在联通块大小

f[u][j] = max(f[u][j], f[u][k]f[v][j-k]);

}

}

}

for(int i = 1; i <= sz[u]; ++i) f[u][0] = max(f[u][0], f[u][i]
i);

}

int main() {

cin >> n;

for(int i = 1, x, y; i <= n-1; ++i) cin >> x >> y, addEdge(x, y), addEdge(y, x);

dp(1, 0);

cout << f[1][0] << endl;

return 0;

}

但是一交上去只有30$pts$,一看讨论区,发现还要用高精度!于是粘了个板子上去,然后就开心的$MLE$了 ̄▽ ̄。最后把$int$换成$short$就对了,无语。
粘一下$AC$代码
```cpp
#include <cstdio>
#include <iostream>
#include <cstring> using namespace std; #define N 700 int n, eid;
short sz[N+5], head[N+5]; struct Edge {
int next, to;
}e[2*N+5]; struct bign{ //高精类模板,网上找的
static const int maxn = 120;
short d[maxn+5];
short len;
void clean() { while(len > 1 && !d[len-1]) len--; }
bign() { memset(d, 0, sizeof(d)); len = 1; }
bign(int num) { *this = num; }
bign(char* num) { *this = num; }
bign operator = (const char* num) {
memset(d, 0, sizeof(d)); len = strlen(num);
for(int i = 0; i < len; i++) d[i] = num[len-1-i] - '0';
clean();
return *this;
}
bign operator = (int num){
char s[20]; sprintf(s, "%d", num);
*this = s;
return *this;
}
bign operator + (const bign& b) {
bign c = *this; int i;
for(i = 0; i < b.len; i++) {
c.d[i] += b.d[i];
if (c.d[i] > 9) c.d[i] %= 10, c.d[i+1]++;
}
while (c.d[i] > 9) c.d[i++] %= 10, c.d[i]++;
c.len = max(len, b.len);
if (c.d[i] && c.len <= i) c.len = i+1;
return c;
}
bign operator - (const bign& b) {
bign c = *this; int i;
for(i = 0; i < b.len; i++) {
c.d[i] -= b.d[i];
if (c.d[i] < 0) c.d[i] += 10, c.d[i+1]--;
}
while (c.d[i] < 0) c.d[i++] += 10, c.d[i]--;
c.clean();
return c;
}
bign operator * (const bign& b) const {
int i, j; bign c; c.len = len + b.len;
for(j = 0; j < b.len; j++)
for(i = 0; i < len; i++)
c.d[i+j] += d[i]*b.d[j];
for(i = 0; i < c.len-1; i++) c.d[i+1] += c.d[i]/10, c.d[i] %= 10;
c.clean();
return c;
}
bign operator / (const bign& b) {
int i, j;
bign c = *this, a = 0;
for(i = len - 1; i >= 0; i--) {
a = a*10 + d[i];
for (j = 0; j < 10; j++)
if (a < b*(j+1)) break;
c.d[i] = j;
a = a - b*j;
}
c.clean();
return c;
}
bign operator % (const bign& b) {
int i, j;
bign a = 0;
for(i = len - 1; i >= 0; i--) {
a = a*10+d[i];
for(j = 0; j < 10; j++) if (a < b*(j+1)) break;
a = a-b*j;
}
return a;
}
bign operator += (const bign& b) {
*this = *this+b;
return *this;
}
bool operator <(const bign& b) const {
if(len != b.len) return len < b.len;
for(int i = len-1; i >= 0; i--)
if(d[i] != b.d[i]) return d[i] < b.d[i];
return false;
}
bool operator >(const bign& b) const { return b < *this; }
bool operator <= (const bign& b) const { return !(b < *this); }
bool operator >= (const bign& b) const { return !(*this < b); }
bool operator != (const bign& b) const { return b < *this || *this < b; }
bool operator == (const bign& b) const { return !(b < *this) && !(b > *this); }
string str() const {
char s[maxn] = {};
for(int i = 0; i < len; i++) s[len-1-i] = d[i]+'0';
return s;
}
}f[N+5][N+5]; istream& operator >> (istream& in, bign& x) {
string s;
in >> s;
x = s.c_str();
return in;
} ostream& operator << (ostream& out, const bign& x) {
out << x.str();
return out;
} void addEdge(int u, int v) {
e[++eid].next = head[u];
e[eid].to = v;
head[u] = eid;
} void dp(int u, int fa) {
sz[u] = 1, f[u][0] = f[u][1] = 1;
for(int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if(v == fa) continue;
dp(v, u);
sz[u] += sz[v];
for(int j = sz[u]; j >= 1; --j) {
for(int k = min(j, sz[u]-sz[v]); k >= max(1, j-sz[v]); --k) {
f[u][j] = max(f[u][j], f[u][k]*f[v][j-k]);
}
}
}
for(int i = 1; i <= sz[u]; ++i) f[u][0] = max(f[u][0], f[u][i]*i);
} int main() {
cin >> n;
for(int i = 1, x, y; i <= n-1; ++i) cin >> x >> y, addEdge(x, y), addEdge(y, x);
dp(1, 0);
cout << f[1][0] << endl;
return 0;
}

洛谷 P1411 树的更多相关文章

  1. 洛谷 P1411 树 (树形dp)

    大意: 给定树, 求删除一些边, 使得连通块大小的乘积最大 设$dp_{i,j}$表示只考虑点$i$的子树, $i$所在连通块大小为$j$的最大值. 转移的时候不计算$i$所在连通块的贡献, 留到最后 ...

  2. 【算法学习】【洛谷】树链剖分 & P3384 【模板】树链剖分 P2146 软件包管理器

    刚学的好玩算法,AC2题,非常开心. 其实很早就有教过,以前以为很难就没有学,现在发现其实很简单也很有用. 更重要的是我很好调试,两题都是几乎一遍过的. 介绍树链剖分前,先确保已经学会以下基本技巧: ...

  3. 洛谷P3384 树链剖分

    如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x ...

  4. 洛谷 P3384 树链剖分(模板题)

    题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...

  5. 洛谷P1268 树的重量

    P1268 树的重量 85通过 141提交 题目提供者该用户不存在 标签树形结构 难度提高+/省选- 提交该题 讨论 题解 记录 最新讨论 有这种情况吗!!!! 题意似乎有问题 题目描述 树可以用来表 ...

  6. 【树链剖分】洛谷P3379 树链剖分求LCA

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  7. 【树链剖分】洛谷P3384树剖模板

    题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...

  8. 洛谷P3248 树 [HNOI2016] 主席树+倍增+分治

    正解:主席树+倍增+分治 解题报告: 传送门! 首先看到这题会想到之前考过的这题 但是那题其实简单一些,,,因为那题只要用个分治+预处理就好,只是有点儿思维难度而已 这题就不一样,因为它说了是按照原树 ...

  9. 洛谷P3368 树状数组2 树状数组+差分

    正解:树状数组+差分 解题报告: 戳我! 不得不说灵巧真滴是越来越弱了...连模板题都要放上来了QAQ 因为今天考试的T3正解要用到树状数组这才惊觉树状数组掌握得太太太太差了...之前一直靠线段树续着 ...

随机推荐

  1. 深入了解Object.defineProperty

    原来写文章都是一次写两三个小时写完,偶尔看到一个人的博客了解到还有草稿箱这个功能,所以以后写文章的时候就舒服多了哈哈,可以存起来再发,不需要一口气写完了 最近一直在看JavaScript高级程序设计, ...

  2. Vue2.5笔记:Vue的实例与生命周期

    理解与认识 Vue 的实例是我们学习 Vue 非常重要的一步,也是非常必须的,因为实例是它的一个起点,也是它的一个入口,只有我们创建一个 Vue 实例之后,我们才行利用它进行一些列的操作. 首先 Vu ...

  3. JIRA笔记(一):安装部署JIRA

    (一)     说明 说明JIRA的安装及破解. 操作系统:WIN 10 数据库:Oracle 12C R2(这个版本的jira,atlassian建议的是 12C R1,不过R2也能用,其他版本不清 ...

  4. Linux分页机制之概述--Linux内存管理(六)

    1 分页机制 在虚拟内存中,页表是个映射表的概念, 即从进程能理解的线性地址(linear address)映射到存储器上的物理地址(phisical address). 很显然,这个页表是需要常驻内 ...

  5. HybridStart混合应用开发框架

    转自我的博客,原文地址:http://refined-x.com/2017/06/26/%E5%9F%BA%E4%BA%8EAPICloud%E7%9A%84%E6%B7%B7%E5%90%88%E5 ...

  6. 【English Email】CIP payouts now in Workday

    simplification简化的[ˌsɪmplɪfɪˈkeɪʃn] quota配额[ˈkwoʊtə]  regional区域的[ˈriːdʒənl]  mechanics技工[məˈkænɪks]  ...

  7. JavaScript 面向对象之原型对象

    原型的概述 我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法. 逻辑上可以这么理解:prototype 通过调用构 ...

  8. ideal中把项目打成war包,并放在tomcat运行,遇见的问题。。。

    先说下我遇见的问题吧:最近做项目要把项目放在tomcat上运行,用的springboot框架, 在建项目时选择的是  jar包,项目写完要部署打包是,在pom中虽然把包改成了war ,可是每次放入to ...

  9. 秒杀系统HTML倒计时设置

    倒计时就是在秒杀表中设定好要秒杀的时间 再减去现在的时间 把时间差显示在页面上 由于第一次搞秒杀倒计时  辛苦搞了半天  留此微博  以留备用 <!DOCTYPE html PUBLIC &qu ...

  10. 解决java compiler level does not match the version of the installed java project facet【转载】

    原博文地址http://blog.csdn.net/chszs/article/details/8125828 Java compiler level does not match the versi ...