题目

题目大意

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

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. 分享安装Apache、MySQL、PHP、LAMP的完整教程

    Operation timed out after 30000 milliseconds with 0 out of -1 bytes received请注意,在Linux中输入密码时,不会显示您输入 ...

  2. thinkphp url生成

    为了配合所使用的URL模式,我们需要能够动态的根据当前的URL设置生成对应的URL地址,为此,ThinkPHP内置提供了U方法,用于URL的动态生成,可以确保项目在移植过程中不受环境的影响. 定义规则 ...

  3. 怎样配置duilib

    duilib是一个免费的界面库,它可利用xml文件自定义界面元素,并且可以在商业项目中无偿使用.怎样在VS中配置duilib界面库呢?请看下面的介绍. 工具/原料 duilib 下载和编译duilib ...

  4. duilib教程之duilib入门简明教程6.XML配置界面

    前面那些教程都是为了让小伙伴们从win32.MFC过渡到duilib,让大家觉得duilib不是那么陌生,如果大家现在还对duilib非常陌生的话,那就说明前面的教程做得不好,请大家在下面留言,我会一 ...

  5. 纯CSS3实现图片展示特效

    本文中要实现的一个纯CSS3的图片展示特效,以前只能用JavaScript实现,可想而知会受到多方面的限制,特别是性能.而今天我们将用简单的CSS3代码实现,你会发现它的动画效果在现代浏览器的帮助下无 ...

  6. <day004>小娜显示空白+CSV文件的基本操作+普通的代理使用

    小知识: 当小娜搜索显示空白的时候,怎么解决? 任务管理器结束小娜进程就好了= =*(多半是惯得,关掉就好了!) 任务1:CSV文件的基本操作 import csv import pandas as ...

  7. iOS开发系列-网络状态监控

    概述 在网络应用中,需要对用户设别的网络状态进行实时监控,可以让用户了解自己的网络状态出现网络问题提示用户. 一般在网络状态不好的场景下需要做一些处理比如: WIFT/3G/4G网络:自动下载高清图. ...

  8. /bin /usr/bin /sbin /usr/sbin 目录的作用

    /bin是系统的一些指令.bin为binary的简写主要放置一些系统的必备执行档例如:cat.cp.chmod df.dmesg.gzip.kill.ls.mkdir.more.mount.rm.su ...

  9. JS数组 组团(如何创建数组)var mychar = new Array( )

    组团,并给团取个名(如何创建数组) 使用数组之前首先要创建,而且需要把数组本身赋至一个变量.好比我们出游,要组团,并给团定个名字"云南之旅". 创建数组语法: var myarra ...

  10. JS规则 是非颠倒(逻辑非操作符)"!"是逻辑非操作符,也就是"不是"的意思,非真即假,非假即真

    是非颠倒(逻辑非操作符) "!"是逻辑非操作符,也就是"不是"的意思,非真即假,非假即真.好比小华今天买了一个杯子,小明说:"杯子是白色的" ...