题目传送门(内部题121)


输入格式

  第一行,一个正整数$n$。
  第二行,$n-1$个正整数$p_2,p_3,...,p_n$。保证$p_u$是在$1$到$u-1$中等概率随机选取的。
  接下来$n$行,第$u$行有$2(n-1)$个数,分别为$a[u][1],b[u][1],...,a[u][u−1],b[u][u-1],a[u][u+1],b[u][u+1],...,a[u][n],b[u][n]$(即去掉$v=u$后的$n-1$对)。
  读入量较大,建议使用读入优化。


输出格式

  一个整数表示能得到整棵树的美丽度的最大值。


样例

样例输入1:

2
1
-71 69
100 -47

样例输出1:

69

样例输入2:

3
1 1
-32 19 84 21
-20 0 7 -86
-37 -33 16 -66

样例输出2:

39


数据范围与提示

样例$1$解释:

  最优方案是点亮$1$不点亮$2$,此时$(1,2)$的贡献为$69$,$(2,1)$没有贡献。

样例$2$解释:

  最优方案是不点亮$1,2$,点亮$3$。

数据范围:

  对于$40\%$的数据,$1\leqslant n\leqslant 16$。
  对于$80\%$的数据,$1\leqslant n\leqslant 200$。
  对于$100\%$的数据,$1\leqslant n\leqslant 1,000,-100\leqslant a[u][v],b[u][v]\leqslant 100$,保证$p_u$是在$1$到$u-1$中等概率随机选取的。


题解

题目中不断强调数据随机,那么我们先挖掘一下其性质:

  $\alpha.$点$u$的期望深度为:$\mathbb{E}d_u=H_{u-1}$(其中调和级数$H=\sum \limits_{i=1}^n\frac{1}{i}=\ln n+\Theta(1)$);其实,你可以简单的将其理解为$u$的期望深度为$1+\ln_{u-1}$。

  $\beta.$点$u$的期望子树大小$\mathbb{E}z_u\leqslant \frac{n}{u}$。

  $\gamma.$树上每个节点的度数是$\log$级别的,这条性质对这道题没有帮助,自动忽略即可。

有了这些性质,考虑如何利用这些性质。

因为期望深度很小,也就是祖先很少,于是我们可以考虑用状压的方式记录其祖先的状态。

判断一棵子树内被点亮的点多还是没有被点亮的点多时需要将被点亮的点的状态存入其中进行转移,但是因为子树的期望大小,所以时间复杂度还是$\Theta(n^2)$级别的。

利用了这些性质,思考进一步解题。

既然已经说了用状压,那八成就是$DP$了,先作出如下定义$\downarrow$

  $\alpha.$设$dp[i][j][s]$表示以$i$为根的子树内有$j$个点被点亮,且从$i$到跟的路径上的点亮状态为$s$的最大贡献。

  $\beta.$设$f[0/1][i][s]$表示当$i$有没有被点亮的情况下从$i$到跟的路径上的点亮状态为$s$的最大贡献。

  $\gamma.$设$g[0/1][i][j]$表示当$i$有没有被点亮的情况下所有与$i$的$lca$为$j$的点的$a$或$b$数组的和。

$g$数组可以在$\Theta(n^2)$(均摊)的时间内暴力求出,注意这里没有必要使用倍增求$lca$。

利用$g$数组可以在$\Theta(n^2)$(均摊)的时间内求出$f$,然后做一个树上背包即可得到$dp$数组,初始$dp[0/1][i][0/1]=f[0/1][i][s]$。

$f$数组有空间问题,可以用$unordered\text{_}map$存储。

最后做三个证明。

$\mathcal{A}.$时间复杂度证明:

$$\begin{array}{ll}\Theta\left(\sum\limits_{u=1}^n2^{\mathbb{E}d_u}(\mathbb{E}z_u)^2\right) &=& \Theta\left(\sum\limits_{u=1}^n2^{\ln u}\left(\frac{n}{u}\right)^2\right) \\ &=& \Theta\left(\sum\limits_{u=1}^nu^{\ln 2}\left(\frac{n}{u}\right)^2\right) \\ &=& \Theta\left(\sum\limits_{u=1}^n\frac{n^2}{u^{2-\ln 2}}\right) \\ &\leqslant& \Theta\left(n^2\sum\limits_{u\geqslant 1}\frac{1}{u^{2-\ln 2}}\right) \\ &=& \Theta(n^2\zeta(2-\ln 2)) \\ &=& \Theta(n^2)\end{array}$$

但是实际上$\mathbb{E}(2^{d_u})\geqslant 2^{\mathbb{E}d_u}$,$\mathbb{E}(z_u^2)\geqslant (\mathbb{E}z_u)^2$,所以实际上时间复杂度大约是$\Theta(\frac{n^2\ln^2n}{\ln\ln n})$的。

$\mathcal{B}.$点$u$的期望深度$d_u$的证明:

边界$d_1=1$,那么:

$$d_u=1+\dfrac{\sum\limits_{v=1}^{u-1}d_v}{u-1}$$

再记:

$$D_u=\sum\limits_{v=1}^ud_v$$

带入上式,得:

$$D_u-D_{u-1}=1+\frac{D_{u-1}}{u-1}$$

整理,得:

$$\frac{D_u}{u}=\frac{1}{u}+\frac{D_{u-1}}{u-1}\Rightarrow\frac{D_u}{u}=H_u$$

所以:

$$d_u=1+\frac{D_{u-1}}{u-1}=1+H_{u-1}=\ln u+\Theta(1)$$

$\mathcal{C}.$点$u$的期望子树大小$z_u$的证明:

边界$z_n=1$,那么可以估算为:

$$z_u=1+\sum\limits_{v=u+1}^n\frac{z_v}{v-1}$$

再记:

$$Z_u=\sum\limits_{v=u}^n\frac{z_u}{v-1}$$

则:

$$z_u=(u-1)(Z_u-Z_{u+1})$$

带入原式,得:

$$(u-1)(Z_u-Z_{u+1})=1+Z_{u+1}$$

整理,得:

$$(u-1)Z_u=1+uZ_{u+1}\Rightarrow(u-1)Z_u=n-u+1$$

即:

$$Z_u=\frac{n-u+1}{u-1}$$

所以:

$$z_u=1+Z_{u+1}=\frac{n}{u}$$

时间复杂度:$\Theta(\frac{n^2\ln^2n}{\ln\ln n})$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec{int nxt,to;}e[1001];
int head[1001],cnt;
int n;
int fa[1001];
int a[1001][1001],b[1001][1001],size[1001],depth[1001];
int dp[2][1001][1001],g[2][1001][20],h[1001][1001];
bool now[1001];
unordered_map<int,int> f[2][1001];
int ans=-0x3f3f3f3f;
void add(int x,int y)
{
e[++cnt].nxt=head[x];
e[cnt].to=y;
head[x]=cnt;
}
void dfs(int x)
{
size[x]=1;
for(int i=head[x];i;i=e[i].nxt)
{
if(depth[e[i].to])continue;
depth[e[i].to]=depth[x]+1;
dfs(e[i].to);
size[x]+=size[e[i].to];
}
}
int LCA(int x,int y)
{
int res=0;
while(depth[y]<depth[x]){x=fa[x];res++;}
while(depth[x]<depth[y])y=fa[y];
while(x!=y){x=fa[x];y=fa[y];res++;}
return res;
}
void dfs(int x,int s)
{
now[x]^=1;
memset(dp[now[x]][x],-0x3f,sizeof(dp[now[x]][x]));
dp[now[x]][x][0]=f[0][x][s];
dp[now[x]][x][1]=f[1][x][s];
int sz=1;
for(int i=head[x];i;i=e[i].nxt)
{
now[x]^=1;
memset(dp[now[x]][x],-0x3f,sizeof(dp[now[x]][x]));
memset(h[e[i].to],-0x3f,sizeof(h[e[i].to]));
dfs(e[i].to,s<<1);
dfs(e[i].to,s<<1|1);
sz+=size[e[i].to];
for(int j=0;j<=size[e[i].to];j++)
for(int k=j;k<=sz;k++)
dp[now[x]][x][k]=max(dp[now[x]][x][k],dp[!now[x]][x][k-j]+h[e[i].to][j]);
}
if(s&1)for(int i=((size[x]+1)>>1);i<=size[x];i++)h[x][i]=dp[now[x]][x][i];
else for(int i=0;i<=((size[x]+1)>>1);i++)h[x][i]=dp[now[x]][x][i];
}
int main()
{
scanf("%d",&n);
for(int i=2;i<=n;i++)
{scanf("%d",&fa[i]);add(fa[i],i);}
depth[1]=1;dfs(1);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j)continue;
scanf("%d%d",&a[i][j],&b[i][j]);
int dep=LCA(i,j);
g[0][i][dep]+=a[i][j];
g[1][i][dep]+=b[i][j];
}
for(int i=1;i<=n;i++)
{
int state=1<<depth[i];
for(int s=0;s<(1<<depth[i]);s++)
for(int j=0;j<depth[i];j++)
if((1<<j)&s)f[1][i][s]+=g[1][i][j];
else f[0][i][s]+=g[0][i][j];
}
dfs(1,0);dfs(1,1);
for(int i=0;i<=n;i++)ans=max(ans,h[1][i]);
printf("%d",ans);
return 0;
}

rp++

[CSP-S模拟测试]:点亮(状压DP+树上背包DP)的更多相关文章

  1. [CSP-S模拟测试]:装饰(状压DP)

    题目传送门(内部题114) 输入格式 第一行一个正整数$n$. 接下来一行$n-1$个正整数,第$i$个数为$f_{i+1}$. 接下来一行$n$个数,若第$i$个数为$0$则表示林先森希望$i$号点 ...

  2. [CSP-S模拟测试]:巨神兵(状压DP)

    题目描述 欧贝利斯克的巨神兵很喜欢有向图,有一天他找到了一张$n$个点$m$条边的有向图.欧贝利斯克认为一个没有环的有向图是优美的,请问这张图有多少个子图(即选定一个边集)是优美的?答案对$1,000 ...

  3. 树形DP和状压DP和背包DP

    树形DP和状压DP和背包DP 树形\(DP\)和状压\(DP\)虽然在\(NOIp\)中考的不多,但是仍然是一个比较常用的算法,因此学好这两个\(DP\)也是很重要的.而背包\(DP\)虽然以前考的次 ...

  4. 6.28 NOI模拟赛 好题 状压dp 随机化

    算是一道比较新颖的题目 尽管好像是两年前的省选模拟赛题目.. 对于20%的分数 可以进行爆搜,对于另外20%的数据 因为k很小所以考虑上状压dp. 观察最后答案是一个连通块 从而可以发现这个连通块必然 ...

  5. 【62测试】【状压dp】【dfs序】【线段树】

    第一题: 给出一个长度不超过100只包含'B'和'R'的字符串,将其无限重复下去. 比如,BBRB则会形成 BBRBBBRBBBRB 现在给出一个区间[l,r]询问该区间内有多少个字符'B'(区间下标 ...

  6. 2018.10.05 NOIP模拟 上升序列(状压dp)

    传送门 状压dp好题. 首先需要回忆O(nlogn)O(nlog n)O(nlogn)求lislislis的方法,我们会维护一个单调递增的ddd数组. 可以设计状态f(s1,s2)f(s1,s2)f( ...

  7. 2018.10.01 NOIP模拟 偷书(状压dp)

    传送门 状压dp经典题. 令f[i][j]f[i][j]f[i][j]表示到第i个,第i−k+1i-k+1i−k+1~iii个物品的状态是j时的最大总和. 然后简单维护一下转移就行了. 由于想皮一下果 ...

  8. 【10.26校内测试】【状压?DP】【最小生成树?搜索?】

    Solution 据说正解DP30行??? 然后写了100行的状压DP?? 疯狂特判,一算极限时间复杂度过不了aaa!! 然而还是过了....QAQ 所以我定的状态是待转移的位置的前三位,用6位二进制 ...

  9. [noip模拟]食物中毒<暴搜+状压优化>

    问题描述 Bqc经过一段时间的研究发现,要解这种毒需要一种特殊的药物.不幸的是,这种药物在 市面上不存在,没有办法Bqc只好亲自制得这种药物.它含有M种化学物质A1,A2,…,AM.现 在Bqc的手上 ...

随机推荐

  1. cut--修剪小能手

    cut命令 cut原理:以每一行位一个处理对象. 参数选项 解释说明 -b 以字节为单位进行分割 -c 以字符为单位进行分割(对切割中文有奇效) -d 自定义分隔符号,默认以tab为分隔符 1:-b ...

  2. LintCode 6---合并排序数组 II

    import java.util.Arrays; public class Lint6 { /* * 合并两个排序的整数数组A和B变成一个新的数组.新数组也要有序. */ public static ...

  3. Django框架——基础之模板系统(template文件夹)

    ---恢复内容开始--- 1. 常用语法 需要记住两组特殊符号:{{  }}  和 {%  %}. 在运用到变量的时候使用{{  }},如果是跟逻辑相关的话就使用{%  %}. 在Django模板(t ...

  4. 第十六篇 JS实现全选操作

    JS实现全选   嗨,同学们好,老师这里是专门教同学解决一些针对性的问题,所以说综合起来,就要靠同学自己了. 这节课我们学一个很实用的东西,全选操作!比如淘宝这种商城对吧,我的购物车,我要全选购买,或 ...

  5. 部署 12306 github 项目

    郑重声明, 本文仅用作学习研究使用,请勿用作商业用途,遵守法律!!! 部署环境有些坑,踩一次就够了... 原项目地址 git clone 原项目以及 识别验证码的模型 如果遇到 无法解析的问题 ,则 ...

  6. Android系统分析之Audio音频流, 音频策略, 输出设备之间的关系

    音频流, 音频策略, 输出设备之间的关系 只针对 AudioManager.STREAM_VOICE_CALL 音频流类型进行分析 涉及到的类: hardware/libhardware_legacy ...

  7. redis性能指标

    1.当内存使用达到设置的最大阀值时,需要选择一种key的回收策略,可在Redis.conf配置文件中修改“maxmemory-policy”属性值. 若是Redis数据集中的key都设置了过期时间,那 ...

  8. 网络初级篇之OSPF(一)原理

    一.OSPF是什么 Open Shortest Path First, 开放最短路径优先协议,是一种开源的使用最短路径优先(SPF)算法的内部网关协议(IGP).常用于路由器的动态选路. 二.OSPF ...

  9. 00:Java简单了解

    浅谈Java之概述 Java是SUN(Stanford University Network),斯坦福大学网络公司)1995年推出的一门高级编程语言.Java是一种面向Internet的编程语言.随着 ...

  10. Hibrenate实现根据实体类自动创建表或添加字段

    Hibernate支持自动建表,在开发阶段很方便,可以保证hbm与数据库表结构的自动同步. 实现: 在配置hibernate的配置文件中将hbm2ddl.auto设置为update,如:Xml代码&l ...