题意

​ 链接: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. 第一节: Redis之String类型和Hash类型的介绍和案例应用

    一. String类型基础 1.类型介绍 典型的Key-Value集合,如果要存实体,需要序列化成字符串,获取的时候需要反序列化一下. 2. 指令Api说明 3.常用Api说明 (1).StringS ...

  2. vue的双向绑定原理浅析与简单实现

    很久之前看过vue的一些原理,对其中的双向绑定原理也有一定程度上的了解,只是最近才在项目上使用vue,这才决定好好了解下vue的实现原理,因此这里对vue的双向绑定原理进行浅析,并做一个简单的实现. ...

  3. 使用 jQuery.AutoComplete 让文本框自动完成

    直接贴代码了. @section headSection { <script type="text/javascript"> $(document).ready(fun ...

  4. 如何配置Memcached高速缓存,加快wordpress的速度

    Memcached是什么 Memcached是一种高性能的分布式内存对象缓存系统.在动态应用,Memcached既能提高访问的速度,同时还减低了数据库的负载.DangaInteractive为提升Li ...

  5. OWIN详细介绍

    1.OWIN.dll介绍 用反编译工具打开Owin.dll,你会发现类库中就只有一个IAppBuilder接口,所以说OWIN是针对.NET平台的开放Web接口. public interface I ...

  6. C typedef、#define

    参考链接:https://www.runoob.com/cprogramming/c-typedef.html 作用 typedef是用来为数据类型(可以是各种数据类型,包括自己定义的数据类型如结构体 ...

  7. Jquery选择器与样式操作

    jquery选择器 jquery用法思想一 选择某个网页元素,然后对它进行某种操作 jquery选择器 jquery选择器可以快速地选择元素,选择规则和css样式相同,使用length属性判断是否选择 ...

  8. mac下chm文件打开乱码解决

    菜单栏点击---显示---文本编码(选择编码格式)

  9. 【IPHONE开发-OBJECTC入门学习】文件的操作,读写复制文件

    转自:http://blog.csdn.net/java886o/article/details/9041547 FileTools.h FileTools.m #import "FileT ...

  10. maven 学习---Eclipse构建Maven项目

    1. 安装m2eclipse插件    要用Eclipse构建Maven项目,我们需要先安装meeclipse插件    点击eclipse菜单栏Help->Eclipse Marketplac ...