[CSP-S模拟测试]:点亮(状压DP+树上背包DP)
题目传送门(内部题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)的更多相关文章
- [CSP-S模拟测试]:装饰(状压DP)
题目传送门(内部题114) 输入格式 第一行一个正整数$n$. 接下来一行$n-1$个正整数,第$i$个数为$f_{i+1}$. 接下来一行$n$个数,若第$i$个数为$0$则表示林先森希望$i$号点 ...
- [CSP-S模拟测试]:巨神兵(状压DP)
题目描述 欧贝利斯克的巨神兵很喜欢有向图,有一天他找到了一张$n$个点$m$条边的有向图.欧贝利斯克认为一个没有环的有向图是优美的,请问这张图有多少个子图(即选定一个边集)是优美的?答案对$1,000 ...
- 树形DP和状压DP和背包DP
树形DP和状压DP和背包DP 树形\(DP\)和状压\(DP\)虽然在\(NOIp\)中考的不多,但是仍然是一个比较常用的算法,因此学好这两个\(DP\)也是很重要的.而背包\(DP\)虽然以前考的次 ...
- 6.28 NOI模拟赛 好题 状压dp 随机化
算是一道比较新颖的题目 尽管好像是两年前的省选模拟赛题目.. 对于20%的分数 可以进行爆搜,对于另外20%的数据 因为k很小所以考虑上状压dp. 观察最后答案是一个连通块 从而可以发现这个连通块必然 ...
- 【62测试】【状压dp】【dfs序】【线段树】
第一题: 给出一个长度不超过100只包含'B'和'R'的字符串,将其无限重复下去. 比如,BBRB则会形成 BBRBBBRBBBRB 现在给出一个区间[l,r]询问该区间内有多少个字符'B'(区间下标 ...
- 2018.10.05 NOIP模拟 上升序列(状压dp)
传送门 状压dp好题. 首先需要回忆O(nlogn)O(nlog n)O(nlogn)求lislislis的方法,我们会维护一个单调递增的ddd数组. 可以设计状态f(s1,s2)f(s1,s2)f( ...
- 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时的最大总和. 然后简单维护一下转移就行了. 由于想皮一下果 ...
- 【10.26校内测试】【状压?DP】【最小生成树?搜索?】
Solution 据说正解DP30行??? 然后写了100行的状压DP?? 疯狂特判,一算极限时间复杂度过不了aaa!! 然而还是过了....QAQ 所以我定的状态是待转移的位置的前三位,用6位二进制 ...
- [noip模拟]食物中毒<暴搜+状压优化>
问题描述 Bqc经过一段时间的研究发现,要解这种毒需要一种特殊的药物.不幸的是,这种药物在 市面上不存在,没有办法Bqc只好亲自制得这种药物.它含有M种化学物质A1,A2,…,AM.现 在Bqc的手上 ...
随机推荐
- 单个html5页面加个密码访问
单个html5页面要实现加个密码才能访问,可以用js来控制.代码加在<head>插入下面代码</head>代码如下: <script languange="Ja ...
- 9.用ExecuteSqlCommand执行存储过程
比如你有一个存储过程 IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[CreateAutho ...
- springboot拦截中自动注入的组件为null问题解决方法
一.写SpringUtil类来获取Springh管理的类实例,判断是否注入成功,如果没有注入成功重新获取注入 package com.util; import org.springframework. ...
- Yii2 常用代码集合
Yii2.0 对数据库查询的一些简单的操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ...
- O015、OpenStack 架构
参考https://www.cnblogs.com/CloudMan6/p/5340622.html 终于正式进入OpenStack 部分了. 今天开始正式学习OpenStack,OpenSt ...
- es分数_score衰减函数
1.按日期衰变 GET news/doc/_search { "query" : { "function_score": { "query" ...
- Vue与Angular以及React的三者之间的区别
1.与AngularJS的区别 相同点:都支持指令:内置指令和自定义指令:都支持过滤器:内置过滤器和自定义过滤器:都支持双向数据绑定:都不支持低端浏览器. 不同点:AngularJS的学习成本高,比如 ...
- mysql数据库查询过程探究和优化建议
查询过程探究 我们先看一下向mysql发送一个查询请求时,mysql做了什么? 如上图所示,查询执行的过程大概可分为6个步骤: 客户端向MySQL服务器发送一条查询请求 服务器首先检查查询缓存,如果命 ...
- linux centos 7安装 apache php 及mariadb
1安装Apache, PHP, MySQL以及php库组件. yum -y install httpd php mysql php-mysql 2 安装apache扩展 yum -y install ...
- Redis常见的七种使用场景
简单字符串缓存实战简单队列实战简单发布订阅实战简单计数器实战排行榜实战简单字符串悲观锁实战简单事务的乐观锁实战 简单字符串缓存实战 $redis->connect('127.0.0.1', ...