题意

​ 链接:https://www.codechef.com/problems/MAXDTREE

​ 给定一个 \(n\) 个节点的树,其中 \(1\) 为根节点,每个点有点权,我们定义“子树”为若干条到根的链的并。给定一个无穷数列 \(\{a_n\}\) ,其构造方法如下:

\[a_n=\begin{cases}
1& n=1\\
a_{n-1}+\text{maxDigit}(a_{n-1})&n>1
\end{cases}
\]

​ 其中 \(\text{maxDigit}(n)\) 函数为 \(n\) 的最大数位。

​ 这个无穷数列的前 \(6\) 项为 \(1,2,4,8,16,22\cdots\) ,求原树有多少个子树先序遍历得到的权值序列包含在 \(\{a_n\}\) 中。

​ \(\displaystyle\sum n\leq 500\)

思路

​ 先考虑如何 \(O(n)\) 判断一个数列在不在 \(\{a_n\}\) 中出现过。

​ 考虑从高位到低位一位一位的填数,我们把填数的状态记作一个 \(xxx00000a\) 形式的数,那么存有几个 \(0\),高位的最大值是多少足矣,这些状态显然是足够表示所有情况的。我们为了转移,就需要一个四维的转移数组,对于一个状态 \(xxx0000a\) ,告诉这个数组最高位的 \(0\) 的位置 、\(xxx\) 中的最大值、个位数 \(a\)、要在最高的 \(0\) 填什么,这个数组就告诉你填完最高位 \(0\) 后(\(xxxx0000a\)),个位数 \(a\) 变成了多少。

​ 接下来考虑怎么 \(\text{dp}\) 得到这个数组,考虑设 \(f[i][j][k]\) 为最高的 \(0\) 为 \(i\) ,\([2,+\infty)\) 的最大数位为 \(j\) ,个位为 \(k\) ,当第 \(i\) 位发生进位时,个位变成了多少,\(g[i][j][k][l]\) 为最高的 \(0\) 为 \(i\) ,\([2,+\infty)\) 的最大数位为 \(j\) ,个位为 \(k\) ,第 \(i\) 位刚刚变成 \(l\) 时,个位变成了多少。可以通过在 \(i-1\) 位不断用 \(f\) 数组进位得到 \(i\) 位的 \(f,g\) 数组,然后就可以转移了。\(g[1]\) 数组的值可以用 \(-1\) 代表这个状态个位达不到,由此判断无解。

​ 上文提到的 \(O(n)\) 判断算法如下:

bool judge(int *num,int len)
{
int p=0,a=1;
DOR(i,len,2)
{
a=g[i][p][a][num[i]];
p=std::max(p,num[i]);
}
return g[1][p][a][num[1]]!=-1;
}

​ 我们接下来考虑如何把这个过程存在 \(dp\) 数组里,在树上进行 \(\rm{dp}\) 。

​ 事实上,上面的函数封闭性已经足够的好,以至于我们可以完全抛下之前 \(\rm{dp}\) 填数的过程,面向 \(\rm{judge}\) 函数进行所谓 \(\rm{dp}\) 套 \(\rm{dp}\) 的过程。

​ 不难发现,按照题目“子树”的定义,当我们填了一个 \(u\) 节点,我们上一个填的节点编号可以为 \([{\rm{dfn}}_{fa_u},{\rm dfn}_u)\) ,以此,我们可以按顺序填数,定义 \(dp[i][j][k][l]\) 为在节点 \(i\) ,\(\rm{judge}\) 函数中的 \(i\) 为 \(j\) ,\(\rm{judge}\) 函数中的 \(p\) 为 \(k\) ,\(\rm{judge}\) 函数中的 \(a\) 为 \(l\) 的方案数,每次转移先枚举这个点,上个点,然后由上个点刷表到这个点。

​ 细节有很多,最重要的是要 “划清内层 \(\rm{dp}\) 的循环过程“。比如我选取 \(\rm{judge}\) 函数的 \(4,5,6,7\) 行作为顺序,然后第 \(3\) 行也可以当成是 \(len+1\) 循环的结束,当然,这样就要在树上的根结点上再加一个特殊节点。

​ 最后由于是个区间的转移,可以前缀和优化,然后就可以通过本题了。

代码

#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;}
template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;}
typedef long long ll;
const int N=505;
const int P=1e9+7;
std::vector<int>G[N];
int f[N][12][12],g[N][12][12][12],dp[N][N][12][12],sdp[N][N][12][12];
int fa[N],lfn[N],ori[N],dfs_idx;
int d[N];
int n,K;
//xxxxx000000a
//f[i][j][k] [2,i]位为0,[i+1,+infty)的最大数位为j,个位为k,第i位进位后个位变成多少
//g[i][j][k][l] [2,i]位为0,[i+1,+infty)的最大数位为j,个位为k,第i位变成l后个位变成多少
//dp[i][j][k][l] 在dfs序为i的节点,judge函数中的i为j,函数中的p为k,函数中的a为l void init()
{
FOR(i,0,K-1)FOR(j,0,K-1)if(i||j)
{
int a=j;
while(a<K)a+=std::max(i,a);
f[1][i][j]=a-K;
}
FOR(i,2,n)FOR(j,0,K-1)FOR(k,0,K-1)if(j||k)
{
int a=k;
FOR(l,0,K-1)
{
g[i][j][k][l]=a;
a=f[i-1][std::max(l,j)][a];
}
f[i][j][k]=a;
}
FOR(i,0,K-1)FOR(j,0,K-1)if(i||j)
{
FOR(k,0,K-1)g[1][i][j][k]=-1;
int a=j;
while(a<K)g[1][i][j][a]=a,a+=std::max(i,a);
}
} bool judge(int *num,int len)
{
int p=0,a=1;
DOR(i,len,2)
{
a=g[i][p][a][num[i]];
p=std::max(p,num[i]);
}
return g[1][p][a][num[1]]!=-1;
} void dfs(int u,int f)
{
fa[u]=f;
ori[lfn[u]=++dfs_idx]=u;
FOR(i,0,(int)G[u].size()-1)
{
int v=G[u][i];
if(v==fa[u])continue;
dfs(v,u);
}
} int main()
{
int T;
scanf("%d",&T);
while(T--)
{
K=10;
scanf("%d",&n);
init(); /*FOR(i,1,100)
{
int num[4],len=0,x=i;
num[++len]=x%10,x/=10;while(x);
printf("i=%d judge(%d)=%d\n",i,i,judge(num,len));
}*/ FOR(i,0,n)G[i].clear();
G[0].push_back(1);
G[1].push_back(0);
FOR(i,1,n-1)
{
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
FOR(i,1,n)scanf("%d",&d[i]);
FOR(i,0,n)std::sort(G[i].begin(),G[i].end()); dfs_idx=0;
dfs(0,-1);
FOR(i,0,n+1)FOR(j,2,n+1)FOR(k,0,K-1)FOR(l,0,K-1)sdp[i][j][k][l]=dp[i][j][k][l]=0;
FOR(i,2,n+1)sdp[1][i][0][1]=dp[1][i][0][1]=1;
int ans=0;
FOR(u,2,n+1)
{
int _u=ori[u];
FOR(i,2,n+1)FOR(j,0,K-1)FOR(k,0,K-1)
{
if(i==2)
{
if(g[1][j][k][d[_u]]!=-1)
{
/*FOR(v,lfn[fa[_u]],u-1)
(ans+=dp[v][i][j][k])%=P;*/
(ans+=(sdp[u-1][i][j][k]-sdp[lfn[fa[_u]]-1][i][j][k]+P)%P)%=P;
}
}
else
{
/*FOR(v,lfn[fa[_u]],u-1)
(dp[u][i-1][std::max(d[_u],j)][g[i-1][j][k][d[_u]]]+=dp[v][i][j][k])%=P;*/
(dp[u][i-1][std::max(d[_u],j)][g[i-1][j][k][d[_u]]]+=(sdp[u-1][i][j][k]-sdp[lfn[fa[_u]]-1][i][j][k]+P)%P)%=P;
}
}
FOR(i,2,n+1)FOR(j,0,K-1)FOR(k,0,K-1)
sdp[u][i][j][k]=(sdp[u-1][i][j][k]+dp[u][i][j][k])%P;
}
printf("%d\n",ans);
}
return 0;
}

CodeChef MAXDTREE(DP套DP)的更多相关文章

  1. bzoj 3864: Hero meet devil [dp套dp]

    3864: Hero meet devil 题意: 给你一个只由AGCT组成的字符串S (|S| ≤ 15),对于每个0 ≤ .. ≤ |S|,问 有多少个只由AGCT组成的长度为m(1 ≤ m ≤ ...

  2. [模板] dp套dp && bzoj5336: [TJOI2018]party

    Description Problem 5336. -- [TJOI2018]party Solution 神奇的dp套dp... 考虑lcs的转移方程: \[ lcs[i][j]=\begin{ca ...

  3. luogu 4158 粉刷匠 dp套dp

    dp套dp 每个木板是个递推的dp,外部是个分组背包 #include<bits/stdc++.h> #define rep(i,x,y) for(register int i=x;i&l ...

  4. DP套DP

    DP套DP,就是将内层DP的结果作为外层DP的状态进行DP的方法. [BZOJ3864]Hero meet devil 对做LCS的DP数组差分后状压,预处理出转移数组,然后直接转移即可. tr[S] ...

  5. Codeforces 372B Counting Rectangles is Fun:dp套dp

    题目链接:http://codeforces.com/problemset/problem/372/B 题意: 给你一个n*m的01矩阵(1 <= n,m <= 40). 然后有t组询问( ...

  6. 【BZOJ3864】Hero meet devil DP套DP

    [BZOJ3864]Hero meet devil Description There is an old country and the king fell in love with a devil ...

  7. codeforces 979E(dp套dp)

    题意: 有n个点,编号为1~n.有的点颜色是黑色,有的点颜色是白色,有的点的颜色待涂.你还可以连一些边,但这些边一定是从小编号连到大编号的点. 对于一个确定的图,我们去统计有多少条路径满足“该路径经过 ...

  8. dp 套 dp扯谈

    1.[扯谈概念] \(dp\) 套 \(dp\) 其实也就是 \(dp\) . 这里就定义下面两个概念: 内层 \(dp\) 表示的是被套在里面的那个 \(dp\) 外层 \(dp\) 表示的是最外面 ...

  9. P4590-[TJOI2018]游园会【dp套dp】

    正题 题目链接:https://www.luogu.com.cn/problem/P4590 题目大意 给出一个长度为\(m\)的字符串\(s\). 对于每个\(k\in[0,m]\)求有多少个长度为 ...

  10. 洛谷 P5279 - [ZJOI2019]麻将(dp 套 dp)

    洛谷题面传送门 一道 dp 套 dp 的 immortal tea 首先考虑如何判断一套牌是否已经胡牌了,考虑 \(dp\)​​​​​.我们考虑将所有牌按权值大小从大到小排成一列,那我们设 \(dp_ ...

随机推荐

  1. 云原生生态周报 Vol. 11 | K8s 1.16 早知道

    业界要闻 Pivotal 发布了完全基于 Kubernetes 的 Pivotal Application Service(PAS)预览版 这意味着 Pivotal 公司一直以来在持续运作的老牌 Pa ...

  2. Rust从入门到放弃(1)—— hello,world

    安装及环境配置 特点:安全,性能,并发 rust源配置 RLS安装 cargo rust管理工具,该工具可以愉快方便的管理rust工程 #!/bin/bash mkdir learn cd learn ...

  3. Kubernetes Secret(机密存储)

    Kubernetes Secret(机密存储) 官方文档:https://kubernetes.io/docs/concepts/configuration/secret/ 加密数据并存放Etcd中, ...

  4. C++调用linux命令并获取返回值

    qt中封装了相关的方法, 但是因为我的命令中用到了管道命令, 出现了非预期结果, 所有改用了linux系统原生的方法. 下边是一个判断某进程是否存在的例子. 当前存在一个问题,当linux返回多行时, ...

  5. 算法笔记 第6章 C++标准模版库(STL)介绍 学习笔记

    6.1 vector的常见用法详解 vector:变长数组,长度根据需要而自动改变的数组 要使用vector,则需要添加vector头文件,即#include<vector>,还需要在头文 ...

  6. git did not exit cleanly (exit code 1) 的解决办法

    问题描述: 关于Git的使用,在通常情况下,习惯于先在本地创建一个本地仓库,然后将项目提交到本地master,再将本地master中的项目Push 到远程仓库中,这样问题就来了. 具体错误信息如下: ...

  7. 搞NDK开发

    1.哪些场景下要用到NDK开发? 跨平台的库,如FFmpeg, skip,weex, 加固,防逆向 签名校验 图片压缩 音视频解码 OpenGL ES 高级特效 热修复 andfix 人脸识别 fac ...

  8. AI人脸识别的测试重点

    最常见的 AI应用就是人脸识别,因此这篇文章从人脸识别的架构和核心上,来讲讲测试的重点. 测试之前需要先了解人脸识别的整个流程,红色标识代表的是对应AI架构中的各个阶段 首先是人脸采集. 安装拍照摄像 ...

  9. Spring事务部分知识点整理

    目录 1.数据库事务基础概念 2.Spring中注解事务的使用 3.Spring事务使用注意场景 1.数据库事务基础概念   数据库事务是对数据库一次一系列的操作组成的单元,可以包含增删改查或者只有单 ...

  10. Java代码实现定时器

    一 import java.util.Timer; import java.util.TimerTask; public class time { public static void main(St ...