JZOJ[5971]【北大2019冬令营模拟12.1】 party(1s,256MB)
题目
题目大意
给你一棵树,在树上的某一些节点上面有人,要用最小的步数和,使得这些人靠在一起。所谓靠在一起,即是任意两个人之间的路径上没有空的节点(也就是连在一起)。
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这个子树中的人的数量)
然后考虑如何转移。
我们分类讨论:
- 如果没有人在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
- 如果有人在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)的更多相关文章
- jzoj5990. 【北大2019冬令营模拟2019.1.6】Bear (状压dp)
题面 题解 我永远讨厌dp.jpg 搞了一个下午优化复杂度最后发现只要有一个小trick就可以A了→_→.全场都插头dp就我一个状压跑得贼慢-- 不难发现我们可以状压,对于每一行,用状态\(S\)表示 ...
- jzoj5991. 【北大2019冬令营模拟2019.1.6】Juice
题面 题解 好迷-- //minamoto #include<bits/stdc++.h> #define R register #define ll long long #define ...
- jzoj5989. 【北大2019冬令营模拟2019.1.6】Forest (set)
题面 题解 为了一点小细节卡了一个下午--我都怕我瞎用set把电脑搞炸-- 观察一次\(1\)操作会造成什么影响,比如说把\(A[i]\)从\(x\)改成\(y\): \(D[x]\)会\(-1\), ...
- jzoj5984. 【北大2019冬令营模拟2019.1.1】仙人掌 (分块)
题面 题解 数据结构做傻了.jpg 考虑每一个节点,它的儿子的取值最多只有\(O(\sqrt {m})\)种,那么可以用一个双向链表维护儿子的所有取值以及该取值的个数,那么对儿子节点修改一个值就是\( ...
- jzoj5983. 【北大2019冬令营模拟2019.1.1】多边形 (组合数学)
这其实是道打表题--你看我代码就知道了-- 咳咳来点严谨证明好了-- 前方高能请注意 首先,正多边形近似于圆,可以看做在圆里内接多边形.圆内接多边形最多只有三个锐角.因为凸多边形的外角和为\(360\ ...
- [JZOJ5977] 【清华2019冬令营模拟12.15】堆
题目 其中n,q≤500000n,q\leq 500000n,q≤500000 题目大意 让你维护一个堆.支持一下操作: 在某个点的下面加上另一个点,然后进行上浮操作. 询问某一点的权值. 思考历程 ...
- [2018冬令营模拟测试赛(二十一)]Problem A: Decalcomania
[2018冬令营模拟测试赛(二十一)]Problem A: Decalcomania 试题描述 输入 见"试题描述" 输出 见"试题描述" 输入示例 见&quo ...
- noip模拟12[简单的区间·简单的玄学·简单的填数]
noip模拟12 solutions 这次考试靠的还是比较好的,但是还是有不好的地方, 为啥嘞??因为我觉得我排列组合好像白学了诶,文化课都忘记了 正难则反!!!!!!!! 害没关系啦,一共拿到了\( ...
- 2019中山纪念中学夏令营-Day9[JZOJ](第六次模拟赛)
Begin (题目的排序方式:Unkown其实是按心情排的) 异或:(摘自百度百科) 异或(xor)是一个数学运算符.它应用于逻辑运算.异或的数学符号为“⊕”,计算机符号为“xor”.其运算法则为: ...
随机推荐
- [JZOJ3303] 【集训队互测2013】城市规划
题目 题目大意 求\(N\)个点的简单无向图的方案数(有编号). 结果对\(1004535809\)取模. 思考历程 感觉这个问题非常经典. 当时想到了一堆式子,但都觉得可能会有重和漏,于是弃掉了-- ...
- python 九九乘法口诀
for i in range(1,10): for j in range(1,i+1): print(j,"*",i,"=",i*j,&qu ...
- 0920CSP-S模拟测试赛后总结
依旧挂了. 这次出题人十分良心.白送了180分……于是我没有拿到.130分rank43滚粗了. T1是道sbdp.然而我死想哈希.然后就死在哈希上了. 我简直是个傻子.之前考了几次字符串的题可以用哈希 ...
- kubernetes配置(kubeconfig)对多集群的访问
配置对多集群的访问 本文展示如何使用配置文件来配置对多个集群的访问. 在将集群.用户和上下文定义在一个或多个配置文件中之后,用户可以使用 kubectl config use-context 命令快速 ...
- web Magic报错 NoSuchMethodError NoSuchMethodError: com.google.common.util.concurrent.SimpleTimeLimiter
webMagic使用selenium的时候遇到报错: java.lang.NoSuchMethodError: com.google.common.util.concurrent.SimpleTime ...
- hibernate_01_SSH环境搭建
1.maven工程pom.xml文件 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="h ...
- 如何用excel统计调查问卷
如何用excel统计调查问卷 有些人可能经常要对调查问卷进行统计,使用手写的方法(就是在纸上写正字的方法)虽然很方便,不过不方便在一定条件下进行统计,如在男士的条件下,使用诺基亚的手机有多少人.而用e ...
- shell常用命令及正则辅助日志分析统计
https://www.cnblogs.com/wj033/p/3451618.html 正则日志分析统计 3 grep 'onerror' v3-0621.log | egrep -v '(\d ...
- iframe 的那些事儿
项目中有不少地方用到iframe,今儿把使用iframe遇到的一些问题一块儿总结一下. 1.javascript监听iframe加载完成事件 iframe加载过程需要一定时间,这个加载过程常常出现白屏 ...
- 第六篇:fastJson常用方法总结
1.了解json json就是一串字符串 只不过元素会使用特定的符号标注. {} 双括号表示对象 [] 中括号表示数组 "" 双引号内是属性或值 : 冒号表示后者是前者的值(这个值 ...