题意

​ 链接: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. Go命令行—compile

    常用作编译命令行指定的单个go源码包.会生成一个以文件.o为后缀的目标文件,其文件名与包内第一个源文件的文件名相同. 目标文件可以与其他对象组合成一个包档案或直接传递给链接器(go tool link ...

  2. sitecore系列教程场所分类简介

    在Sitecore体验平台(XP)中,场所是可跟踪的离线交互发生的位置.这些是发生交互的物理位置,例如特定的零售场所或公共汽车站. 您可以使用场所分类记录特定交互发生的位置.此信息保存在体验数据库(x ...

  3. Codeforces 939A题,B题(水题)

    题目链接:http://codeforces.com/problemset/problem/939/A A题 A. Love Triangle time limit per test 1 second ...

  4. Python基础22

    数据类型可变不可变,说的是“指向”. 深浅拷贝.

  5. Vue.js 源码分析(七) 基础篇 侦听器 watch属性详解

    先来看看官网的介绍: 官网介绍的很好理解了,也就是监听一个数据的变化,当该数据变化时执行我们的watch方法,watch选项是一个对象,键为需要观察的数据名,值为一个表达式(函数),还可以是一个对象, ...

  6. swift(二)swift字符串和字符和逻辑运算

    /* 1.swift字符串和字符 2.构造字符串 3.字符串比较 4.数值运算 5.复制运算 6.关系运算 7.逻辑运算 8.区间运算 */ /* //数据 + 数据的处理 //字符信息+ 字符信息的 ...

  7. Android View篇之自定义验证码输入框

    首先,我们来看看实现的是怎么样的效果: 如果我们拿到这样的UI,想到的布局应该是用4个EditText包在横向的LinearLayout里面,但今天要讲的View,所以我们决定用一个自定义的EditT ...

  8. [20190507]sga_target=0注意修改_kghdsidx_count设置.txt

    [20190507]sga_target=0注意修改_kghdsidx_count设置.txt --//昨天遇到一例视图定义太复杂导致长时间分析sql语句出现library cache lock等待事 ...

  9. Mysql时区无法识别

    Unable to connect to database. Tried 1 times {:error_message=>“Java::JavaSql::SQLException: The s ...

  10. Java八大排序之插入排序

    插入排序 也可叫直接插入排序,该算法的思路是:初始可认为文件中的第1个记录已排好序,然后将第2个到第n个记录依次插入到已排序的记录组成的文件中. 步骤: 假设有一组数组为(数组下标0—n-1): ar ...