题目

题目大意

给你一棵树,在树上的某一些节点上面有人,要用最小的步数和,使得这些人靠在一起。所谓靠在一起,即是任意两个人之间的路径上没有空的节点(也就是连在一起)。

N≤200N \leq 200N≤200


思考历程

看了题目好久,没有什么思路。

想到DP,但不知道怎么用DP做。

然后去翻翻题解,然后一脸懵逼,再去问问几位大佬。

LYL、ZHJ、GMH这三个大爷都说这题很水,是联赛难度。

不屑于给我讲。

天哪,这就是人与人之间的差距!太恐怖了。

然后我只能依靠我自己硬是刚了四天,对,是四天。

终于搞了出来……


正解

首先这题是一个树形DP。

看到这题的数据范围这么小,嗯,先估计一个时间复杂度。

就是O(N3)O(N^3)O(N3)了

就这么随意

然后想想DP的状态

设fi,jf_{i,j}fi,j​表示在iii这个节点的子树中(除iii以外),留下了jjj个人的最优解。(**在这个时候,其它的人被丢到了iii处

所谓的jjj个东西要和iii接在一起。

注意,对于iii这棵子树,有可能会有其它的节点进去里面,所以,这个jjj可以大于sumsonsum_{son}sumson​(sumisum_isumi​表示iii这个子树中的人的数量)

然后考虑如何转移。

我们分类讨论:

  1. 如果没有人在sonsonson这个节点上,那么sonsonson这棵子树里面的人一定都会跑出来(不然就断了),所以留下的为空。那么此时fx,i=fson,0+sumson+fx,if_{x,i}=f_{son,0}+sum_{son}+f_{x,i}fx,i​=fson,0​+sumson​+fx,i​
  2. 如果有人在sonsonson这个节点上,设留在sonsonson这棵子树内的人数为jjj。显然j≠0j \neq 0j̸​=0

    注意一下我设的这个状态,你会发现这个状态不包括这个根节点。

    如果有人在sonsonson这个节点上,那么子树剩余的人数为j−1j-1j−1。

    由于有sumson−jsum_{son}-jsumson​−j个人要出来,所以加上它们的贡献。注意,有可能是进去的,所以要用绝对值。

    fx,i=fson,j−1+∣sumson−j∣+fx,i−jf_{x,i}=f_{son,j-1}+|sum_{son}-j|+f_{x,i-j}fx,i​=fson,j−1​+∣sumson​−j∣+fx,i−j​

DP搞完了,然后呢?每次换根重新操作?

当然不存在的,那样就有可能T飞了。

我们枚举每一个节点,将其作为最后形成的块的顶端。

在它子树内的东西都已经搞好了,所以,只需要搞一搞子树外的东西。

然后我们就可以发现只需要将它子树外的人到它距离之和加起来就好了。

因为这个点是整块的顶端,所以那些点必定会钻到下面去。

钻到下面之前肯定要先来到这个地方,剩下的就加上DP出来的结果就行了。

那么用什么来处理这个距离的和呢?

一开始,由于强迫症,我想用一些好的方法。

后来想想,反正范围这么小,那么简单粗暴一下就好了。

直接枚举子树外面的节点,然后一个一个地将距离加上(Floyed预处理)。

判断是否在子树内用dfndfndfn序

时间复杂度O(N3)O(N^3)O(N3)

是不是和预想的一模一样


其实这个题真的有点奇怪,的确不是很难,但是容易误入歧途。

在一开始,我设的状态为:fi,jf_{i,j}fi,j​表示在iii的子树内,留下jjj个。

和最后的状态有什么区别呢?区别其实不大,就是包不包括iii这个节点。

然后我发现这样设繁琐至极,自己在推理的过程中绕来绕去的,很晕。

因为在这个东西中,我需要考虑一下根节点有没有人。

如果一开始没有人,那就要拿下面的补上,然后我又想着要开多一维来记录目前的这一块中的深度最小的点,用它来补上去,然后特别麻烦,怎么转移都不知道。

后来想想,我的DP可以设再设一下,表示根节点是否有东西……

我们知道在最后根节点一定有东西,或者所有的东西都跑到外面去,不可能出现根节点为空,而它的儿子的子树中还有东西。

那么在转移时还是有点麻烦。

最后,经过GMH的一点指导,然后我就又想了好久,才想了出来。

状态的这一点改动,实际上是解法的一个大大的更新。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#define N 200
#define INF 400000
int n;
char a[N+3];
struct EDGE{
int to;
EDGE *las;
} e[N*2+1];
int ne;
EDGE *last[N+1];
inline void link(int u,int v){
++ne;
e[ne].to=v,e[ne].las=last[u];
last[u]=e+ne;
}
int dfn[N+1],nowdfn;
int siz[N+1],sum[N+1];
int f[N+1][N+1];
void dp(int,int);
int dis[N+1][N+1];
void dfs(int,int);
int ans;
int main(){
freopen("party.in","r",stdin);
freopen("party.out","w",stdout);
scanf("%d%s",&n,a+1);
for (int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
link(u,v),link(v,u);
}
for (int i=1;i<=n;++i)
fill(f[i],f[i]+n+1,INF);
dp(1,0);
for (int i=1;i<=n;++i)
fill(dis[i],dis[i]+n+1,INF);
for (int i=1;i<=n;++i)
dis[i][i]=0;
for (int i=1;i<=n;++i)
for (EDGE *ei=last[i];ei;ei=ei->las)
dis[i][ei->to]=dis[ei->to][i]=1;
for (int k=1;k<=n;++k)
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
ans=INF;
dfs(1,0);
printf("%d\n",ans);
return 0;
}
void dp(int x,int fa){
dfn[x]=++nowdfn;
siz[x]=1;
if (a[x]=='0')
sum[x]=0;
else
sum[x]=1;
f[x][0]=0;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa){
dp(ei->to,x);
siz[x]+=siz[ei->to],sum[x]+=sum[ei->to];
for (int i=siz[x]-1;i>=0;--i){//i倒着枚举,就像是背包问题一样。另外,由于目前的节点总数为siz[x],而x不算,所以最多为siz[x]-1
//转移,具体理由见上
f[x][i]=f[ei->to][0]+sum[ei->to]+f[x][i];
for (int j=1;j<=i;++j)
f[x][i]=min(f[x][i],f[ei->to][j-1]+abs(sum[ei->to]-j)+f[x][i-j]);
}
}
}
void dfs(int x,int fa){
int s=0;
for (int i=1;i<=n;++i)
if (a[i]=='1' && !(dfn[x]<=dfn[i] && dfn[i]<dfn[x]+siz[x]))//后面的这个东西判断它是否在子树里面
s+=dis[i][x];
ans=min(ans,s+f[x][sum[1]-1]);//因为总共的人数是sum[1],而有一个在x上,所以为sum[1]-1
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa)
dfs(ei->to,x);
}

总结

首先,在膜拜一下三个大佬。

还有后来随随便便AC的ZSH、PZM大佬。

对于在一棵树上又看起来不好用数据结构什么的东西来做的题目的时候,就想想能不能树形DP。

对于这题,树形DP的最大的妙处在于根节点不包括在内。

根节点有时候会处于一种比较尴尬的状况,所以,我们就要对它特殊地对待。

然后在状态中就把根节点排除在外,暂时不考虑。

其它的事

我翻了翻几位大佬的代码,都是枚举根节点然后搞一遍DP。

你们怎么不会TLE?而且,为什么,为什么时间还比我短!!!

有种被欺骗了的感觉。

还有这题的数据是真的水。

我的代码之前有一个很严重的错误,然而,带着这个错误,我居然有90分!

So strange!

这个错误是,Floyed中初值没有赋值好……

所以我不保证我现在这个代码一定是对的,但这个思想还是值得借鉴。

JZOJ[5971]【北大2019冬令营模拟12.1】 party(1s,256MB)的更多相关文章

  1. jzoj5990. 【北大2019冬令营模拟2019.1.6】Bear (状压dp)

    题面 题解 我永远讨厌dp.jpg 搞了一个下午优化复杂度最后发现只要有一个小trick就可以A了→_→.全场都插头dp就我一个状压跑得贼慢-- 不难发现我们可以状压,对于每一行,用状态\(S\)表示 ...

  2. jzoj5991. 【北大2019冬令营模拟2019.1.6】Juice

    题面 题解 好迷-- //minamoto #include<bits/stdc++.h> #define R register #define ll long long #define ...

  3. jzoj5989. 【北大2019冬令营模拟2019.1.6】Forest (set)

    题面 题解 为了一点小细节卡了一个下午--我都怕我瞎用set把电脑搞炸-- 观察一次\(1\)操作会造成什么影响,比如说把\(A[i]\)从\(x\)改成\(y\): \(D[x]\)会\(-1\), ...

  4. jzoj5984. 【北大2019冬令营模拟2019.1.1】仙人掌 (分块)

    题面 题解 数据结构做傻了.jpg 考虑每一个节点,它的儿子的取值最多只有\(O(\sqrt {m})\)种,那么可以用一个双向链表维护儿子的所有取值以及该取值的个数,那么对儿子节点修改一个值就是\( ...

  5. jzoj5983. 【北大2019冬令营模拟2019.1.1】多边形 (组合数学)

    这其实是道打表题--你看我代码就知道了-- 咳咳来点严谨证明好了-- 前方高能请注意 首先,正多边形近似于圆,可以看做在圆里内接多边形.圆内接多边形最多只有三个锐角.因为凸多边形的外角和为\(360\ ...

  6. [JZOJ5977] 【清华2019冬令营模拟12.15】堆

    题目 其中n,q≤500000n,q\leq 500000n,q≤500000 题目大意 让你维护一个堆.支持一下操作: 在某个点的下面加上另一个点,然后进行上浮操作. 询问某一点的权值. 思考历程 ...

  7. [2018冬令营模拟测试赛(二十一)]Problem A: Decalcomania

    [2018冬令营模拟测试赛(二十一)]Problem A: Decalcomania 试题描述 输入 见"试题描述" 输出 见"试题描述" 输入示例 见&quo ...

  8. noip模拟12[简单的区间·简单的玄学·简单的填数]

    noip模拟12 solutions 这次考试靠的还是比较好的,但是还是有不好的地方, 为啥嘞??因为我觉得我排列组合好像白学了诶,文化课都忘记了 正难则反!!!!!!!! 害没关系啦,一共拿到了\( ...

  9. 2019中山纪念中学夏令营-Day9[JZOJ](第六次模拟赛)

    Begin (题目的排序方式:Unkown其实是按心情排的) 异或:(摘自百度百科) 异或(xor)是一个数学运算符.它应用于逻辑运算.异或的数学符号为“⊕”,计算机符号为“xor”.其运算法则为: ...

随机推荐

  1. 导入导出sql结构和数据

    导入导出sql结构和数据

  2. JavaScript 数据值校验工具类

    /** * 数据值校验工具类 */ var checkService = { // 不校验 none: function () { return true; }, //非空校验 isEmpty: fu ...

  3. 关于web前端网站优化

    不知道是哪位大牛的文章,转过来嘻嘻. 作者:斯迪链接:https://www.zhihu.com/question/21658448/answer/18903129来源:知乎著作权归作者所有.商业转载 ...

  4. 尚学linux课程---10、linux环境下安装python

    尚学linux课程---10.linux环境下安装python 一.总结 一句话总结: 直接在官网下载python的源码包即可,然后在linux下安装 linux下安装软件优先想到的的确是yum,但是 ...

  5. selenium基础(多表单切换、多窗口切换)

    一.多表单的切换 frame:HTML页面中的一中框架,主要作用是在当前页面中指定区域显示另一页面元素:              (HTML语言中,frame/iframe标签为表单框架) 在web ...

  6. web系统基础

    网络标准体系架构 B/S(browser/server浏览器)服务器有iis.apache.Tomcat.Ngix.Lighttp等 C/S(client/server客户端)如微信.QQ.Outlo ...

  7. windows10 vs2019 + opencv 3.4.7环境搭建

    windows vs2019 + opencv 3.4.7环境搭建 安装Opencv 3.4.7 下载 Opencv 第1步 进入 opencv releases 页面,点击 "Window ...

  8. python requests 高级用法

    高级用法 本篇文档涵盖了 Requests 的一些高级特性. 会话对象 会话对象让你能够跨请求保持某些参数.它也会在同一个 Session 实例发出的所有请求之间保持 cookie, 期间使用 url ...

  9. CentOS安装tomcat并且部署Java Web项目具有一定的参考价值

    本篇文章主要介绍了CentOS安装tomcat并且部署Java Web项目,具有一定的参考价值,有需要的可以了解一下.(http://m.8682222.com) 1.准备工作 b.因为tomcat的 ...

  10. 廖雪峰Java12maven基础-2maven进阶-1使用插件

    1.maven的Lifecycle,Phase和Goal: 使用maven构建项目就是执行Lifecycle 执行Lifecycle就是按顺序执行一系列Phase 每执行一个Phase,都会执行该Ph ...