洛谷 P1411 树
最近在做些树形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 树的更多相关文章
- 洛谷 P1411 树 (树形dp)
大意: 给定树, 求删除一些边, 使得连通块大小的乘积最大 设$dp_{i,j}$表示只考虑点$i$的子树, $i$所在连通块大小为$j$的最大值. 转移的时候不计算$i$所在连通块的贡献, 留到最后 ...
- 【算法学习】【洛谷】树链剖分 & P3384 【模板】树链剖分 P2146 软件包管理器
刚学的好玩算法,AC2题,非常开心. 其实很早就有教过,以前以为很难就没有学,现在发现其实很简单也很有用. 更重要的是我很好调试,两题都是几乎一遍过的. 介绍树链剖分前,先确保已经学会以下基本技巧: ...
- 洛谷P3384 树链剖分
如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x ...
- 洛谷 P3384 树链剖分(模板题)
题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...
- 洛谷P1268 树的重量
P1268 树的重量 85通过 141提交 题目提供者该用户不存在 标签树形结构 难度提高+/省选- 提交该题 讨论 题解 记录 最新讨论 有这种情况吗!!!! 题意似乎有问题 题目描述 树可以用来表 ...
- 【树链剖分】洛谷P3379 树链剖分求LCA
题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...
- 【树链剖分】洛谷P3384树剖模板
题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...
- 洛谷P3248 树 [HNOI2016] 主席树+倍增+分治
正解:主席树+倍增+分治 解题报告: 传送门! 首先看到这题会想到之前考过的这题 但是那题其实简单一些,,,因为那题只要用个分治+预处理就好,只是有点儿思维难度而已 这题就不一样,因为它说了是按照原树 ...
- 洛谷P3368 树状数组2 树状数组+差分
正解:树状数组+差分 解题报告: 戳我! 不得不说灵巧真滴是越来越弱了...连模板题都要放上来了QAQ 因为今天考试的T3正解要用到树状数组这才惊觉树状数组掌握得太太太太差了...之前一直靠线段树续着 ...
随机推荐
- SQL Server 数据库部分常用语句小结(二)
9. 查询备份还原数据库的进度. select command ,percent_complete ,est_time_to_go=convert(varchar,(estimated_complet ...
- 【公众号系列】两分钟学会SAP F1技巧
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[公众号系列]两分钟学会SAP F1技巧 写 ...
- linux 网络套接字
在内核分析网络分组时,底层协议的数据将传输到跟高的层.而发送数据的时候顺序是相反的.每一层都是通过加(首部+净荷)传向跟底层,直至最终发送. 这些操作决定了网络的的性能. 就如下图所示 linux因此 ...
- Cs231n课堂内容记录-Lecture 8 深度学习框架
Lecture 8 Deep Learning Software 课堂笔记参见:https://blog.csdn.net/u012554092/article/details/78159316 今 ...
- Sublime3如何用快捷键实现字母的大小写转换
说明 有的时候需要将字母大小写一键转换一下,很显然,通过编辑器来实现会更加轻量级,而不是打开IDE去实现 我用的Sublime3版本如下: 步骤 1.打开Sublime的Key Bindings 2. ...
- apache https配置【转】
博文来源:apache https配置 参考博文:apache.nginx配置自签名证书 1. 确认是否安装ssl模块 是否有mod_ssl.so文件 2. 生成证书和密钥 linux下 步骤1: ...
- SQL解析在美团的应用
https://tech.meituan.com/SQL_parser_used_in_mtdp.html 数据库作为核心的基础组件,是需要重点保护的对象.任何一个线上的不慎操作,都有可能给数据库带来 ...
- MongoDB install
下载地址1:https://www.mongodb.org/dl/linux下载地址2:https://www.mongodb.com/download-center/community关于Mongo ...
- Mongo字符串类型的数值查询---$Where查询介绍
在Mongo中都知道字符串类型大小比较都是以ASCII进行比较的,所以无法真实比较字符串类型的数值大小 比如查询age大于3的: db.getCollection(&q ...
- 第十一节,利用yolov3训练自己的数据集
1.环境配置 tensorflow1.12.0 Opencv3.4.2 keras pycharm 2.配置yolov3 下载yolov3代码:https://github.com/qqwweee/k ...