洛谷 P5853 - [USACO19DEC]Tree Depth P(生成函数+背包)
神仙题。
首先考虑一个点的深度是什么,注意到对于笛卡尔树而言直接从序列的角度计算一个点的深度是不容易的,因为这样会牵扯到序列中多个元素,需要 fixed 的东西太多,计算起来太复杂了。因此考虑从树本身的角度计算一个点的深度。注意到对于一棵树上所有点 \(u\) 而言都有 \(dep_u=\sum\limits_{v}[\text{LCA}(u,v)=v]\),因此我们求解一个点 \(x\) 的答案时,可以枚举所有 \(u,v\) 并计算 \(v\) 对 \(u\) 的贡献,即,有多少个排列满足逆序对个数为 \(k\),且笛卡尔树上 \(v\) 为 \(u\) 的祖先。并且我们还可以注意到,对于一个序列而言,其笛卡尔树上某两点存在祖先关系是存在充要条件转化的,具体来说,\(\text{LCA}(u,v)=v\) 当且仅当 \(a_v\) 为 \(a[\min(u,v)…\max(u,v)]\) 的最小值,因此我们只需求解以下问题:
有多少个排列 \(p\),满足其逆序对个数为 \(k\),且 \(a_v=\min\limits_{i=\min(u,v)}^{\max(u,v)}a_i\)
这个问题看似难以下手,因为外面就已经枚举了两维 \(u,v\) 了,里面这东西也无法直接组合数求解,需要用 DP 之类的东西,一弄最低就是 \(n^3\),直接爆炸,不过细想其实不用复杂度那么高。我们首先考虑如果没有第二个条件怎么求,我们设 \(dp_{i,j}\) 表示有多少个长度为 \(i\) 的排列有 \(j\) 个逆序对,考虑怎么转移,不难发现,对于长度为 \(i+1\) 的排列,我们总能找到唯一的 \(a_{i+1}\),满足 \(a_{i+1}\) 与前面 \(i\) 个数产生的逆序对数为 \(x(x\in[0,i])\),也就是说 \(dp_{i+1,j}=\sum\limits_{x=0}^idp_{i,j-x}\),前缀和优化一下即可,这个在 CF1542E2 Abnormal Permutation Pairs (hard version) 中就已经见过了。对于此题亦是如此,与经典问题不同的一点是,直接按照 \(1,2,3,\cdots,n\) 的位置填数会爆炸,因此考虑换个顺序,如果 \(u<v\) 那么我们就按照 \(u,u+1,u+2,\cdots,v,u-1,u-2,\cdots,1,v+1,v+2,\cdots,n\) 的顺序填数,否则我们按照 \(u,u-1,u-2,\cdots,v,u+1,u+2,\cdots,n,v-1,v-2,\cdots,1\) 的顺序填。不难发现按照这样的顺序填数之后,其他位置上的填法都和前面一样,即第 \(i\) 个填的数可以为排列逆序对数产生 \([0,i-1]\) 中任意一个数的贡献,唯独 \(v\) 只有一种填法。而两种情况的差别的,前一种情况 \(v\) 会对排列逆序对数产生 \(|u-v|\) 的贡献,而后一种不会,因此如果写成生成函数的形式,就是前一种情况的方案数为 \([x^k]·x^{|u-v|}\prod\limits_{i=0}^{|u-v|-1}(\sum\limits_{j=0}^ix^j)·\prod\limits_{i=|u-v|+1}^{n-1}(\sum\limits_{j=0}^ix^j)\),后一种情况的方案数为 \(x^{|u-v|}\prod\limits_{i=0}^{|u-v|-1}(\sum\limits_{j=0}^ix^j)·\prod\limits_{i=|u-v|+1}^{n-1}(\sum\limits_{j=0}^ix^j)\),注意到对于一对 \(u,v\) 而言,上面两个式子的值只与 \(|u-v|\) 有关,因此可以对所有 \(|u-v|\) 预处理一波答案,复杂度 \(\mathcal O(n^4)\)。如果你观察能力再强一些,你还能发现这式子可以写成背包的形式,你对前后缀各做一遍背包就可以 \(n^3\) 求解了。
const int MAXN=300;
const int MAXK=44850;
int n,k,mod;
int pre[MAXN+5][MAXK+5],suf[MAXN+5][MAXK+5];
int sum[MAXK+5],c1[MAXN+5],c2[MAXN+5];
int getsum(int l,int r){return (sum[r]-((!l)?0:sum[l-1])+mod)%mod;}
int main(){
scanf("%d%d%d",&n,&k,&mod);pre[0][0]=1;
for(int i=0;i<=k;i++) sum[i]=1;
for(int i=1;i<n;i++){
for(int j=0;j<=k;j++) pre[i][j]=getsum(j-i,j);
memset(sum,0,sizeof(sum));sum[0]=pre[i][0];
for(int j=1;j<=k;j++) sum[j]=(sum[j-1]+pre[i][j])%mod;
} suf[n][0]=1;
for(int i=0;i<=k;i++) sum[i]=1;
for(int i=n-1;~i;i--){
for(int j=0;j<=k;j++) suf[i][j]=getsum(j-i,j);
memset(sum,0,sizeof(sum));sum[0]=suf[i][0];
for(int j=1;j<=k;j++) sum[j]=(sum[j-1]+suf[i][j])%mod;
} c1[0]=c2[0]=pre[n-1][k];
for(int i=1;i<n;i++){
for(int j=0;j<=k;j++) c1[i]=(c1[i]+1ll*pre[i-1][j]*suf[i+1][k-j])%mod;
for(int j=0;j<=k-i;j++) c2[i]=(c2[i]+1ll*pre[i-1][j]*suf[i+1][k-i-j])%mod;
} for(int i=1;i<=n;i++){
int res=0;
for(int j=1;j<=n;j++){
if(j<=i) res=(res+c1[i-j])%mod;
else res=(res+c2[j-i])%mod;
} printf("%d%c",res," \n"[i==n]);
}
return 0;
}
洛谷 P5853 - [USACO19DEC]Tree Depth P(生成函数+背包)的更多相关文章
- 【题解】洛谷P1273 有线电视网(树上分组背包)
次元传送门:洛谷P1273 思路 一开始想的是普通树形DP 但是好像实现不大好 观摩了一下题解 是树上分组背包 设f[i][j]为以i为根的子树中取j个客户得到的总价值 我们可以以i为根有j组 在每一 ...
- 【洛谷】P1541 乌龟棋(四维背包dp)
题目背景 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 题目描述 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起 ...
- 【题解】洛谷P1941 [NOIP2014TG] 飞扬的小鸟(背包DP)
次元传送门:洛谷P1941 思路 从题意可知 在每个单位时间内 可以无限地向上飞 但是只能向下掉一次 所以我们可以考虑运用背包解决这道题 上升时 用完全背包 下降时 用01背包 设f[x][y]为在坐 ...
- 【题解】洛谷P1541 [NOIP2010TG] 乌龟棋(类似背包的DP)
题目来源:洛谷P1541 思路 类似背包的题 总之就是四种卡牌取的先后顺序不同导致的最终ans不同 所以我们用一个四维数组每一维分别表示第几种取了几张的最大分数 然后就是简单DP解决 代码 #incl ...
- 洛谷P5206 [WC2019] 数树(生成函数+容斥+矩阵树)
题面 传送门 前置芝士 矩阵树,基本容斥原理,生成函数,多项式\(\exp\) 题解 我也想哭了--orz rqy,orz shadowice 我们设\(T1,T2\)为两棵树,并定义一个权值函数\( ...
- [USACO19DEC]Tree Depth P
题意 求逆序对为\(k\)的\(n\)排列中,生成的笛卡尔数,每个位置的深度和.\(n\le 300\) 做法 设\(f_{k}\)为\(n\)排列中逆序对为\(k\)的个数,其生成函数为:\[\pr ...
- 【洛谷P4178】Tree
题面 题解 感觉和\(CDQ\)分治一样套路啊 首先,构建出点分树 对于每一层分治重心,求出它到子树中任意点的距离 然后\(two-pointers\)计算满足小于等于\(K\)的点对数目,加入答案 ...
- BZOJ2654 & 洛谷2619:tree——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=2654 https://www.luogu.org/problemnew/show/P2619 给你 ...
- 洛谷4178 BZOJ1468 Tree题解点分治
点分治的入门练习. 题目链接 BZOJ的链接(权限题) 关于点分治的思想我就不再重复了,这里重点说一下如何判重. 我们来看上图,假设我们去除了1节点,求出d[2]=1,d[3]=d[4]=2 假设k为 ...
随机推荐
- [Git系列] 前言
Git 简介 Git 是一个重视速度的分布式版本控制和代码管理系统,最初是由 Linus Torvalds 为开发 Linux 内核而设计并开发的,是一款遵循二代 GUN 协议的免费软件.这一教程会向 ...
- 第6次 Beta Scrum Meeting
本次会议为Beta阶段第6次Scrum Meeting会议 会议概要 会议时间:2021年6月8日 会议地点:「腾讯会议」线上进行 会议时长:15min 会议内容简介:对完成工作进行阶段性汇报:对下一 ...
- [no code][scrum meeting] Alpha 2
项目 内容 会议时间 2020-04-07 会议主题 功能规格说明书review 会议时长 30min 参会人员 OCR组(肖思炀,赵涛)和产品经理 $( "#cnblogs_post_bo ...
- Machine learning (6-Logistic Regression)
1.Classification However, 2.Hypothesis Representation Python code: import numpy as np def sigmoid(z) ...
- 看动画学算法之:双向队列dequeue
目录 简介 双向队列的实现 双向队列的数组实现 双向队列的动态数组实现 双向队列的链表实现 双向链表的时间复杂度 简介 dequeue指的是双向队列,可以分别从队列的头部插入和获取数据,也可以从队列的 ...
- uvm_subscriber
subscriber是消费,用户的意思 uvm_subscriber主要作为coverage的收集方式之一 uvm_subscriber的代码非常简单,继承于uvm_component,再加上一个an ...
- 关于docker中容器可以Ping通外网,真机无法Ping通容器的问题
首先我们要知道整体的框架结构,docker是我们安装在centos7上的,而centos7是安装在vmware上.其中docker中还有若干容器运行. 整体框架图如下: 我们将它分为两部分,一部分是d ...
- Ubuntu 安装 mysql 报错 "update-alternatives: 错误: 候选项路径 /etc/mysql/mysql.cnf 不存在"
解决方法: sudo cp /etc/mysql/my.cnf /etc/mysql/mysql.cnf 偷梁换柱-! 如果想更新mysql的源方法如下: wget http://dev.mysql. ...
- Java使用assert断言
Java1.4后新增assert关键字 Idea中开启assert断言 使用 assert boolean表达式 assert boolean表达式 : 错误提示信息 例子 public static ...
- 四. 几个Promise常用API的介绍与使用
四. 几个常用API的介绍与使用 1. Promise构造函数:Promise(excutor){} excutor函数:同步执行 (resolve, reject) => {} resolve ...