题面

请务必不要吐槽我的标签

传送门

思路

一个很重要的结论:原序列的一组同构的解等价于同一棵拥有$n$个节点的笛卡尔树

注意笛卡尔树的定义:父亲节点是区间最值,并且分割区间为左右部分

所以如果两个序列的笛卡尔树同构,那么他们的每一个区间最小值位置相同,也就是原题目中的同构条件了

一个很重要的结论:定义笛卡尔树节点的深度为根到这个节点的路径上向左走的次数,那么合法序列的笛卡尔树所有节点深度不超过$m$

首先,我们可以定义区间的父节点是所有最值中最靠左的,那么容易得到,节点的左儿子中的所有权值严格小于当前节点

这样,我们往左走的次数一旦超过了$m$就意味着有$m$以上个不同的数出现在序列中

反之,我们可以证明对于一个没有深度超过$m$的节点的笛卡尔树一定能构造出一组合法的,$m$个数都被用过的解

首先,找到笛卡尔树上的最深链(设其长度为$len$),并把最长链上的节点构造成为$n$到$n-len+1$

然后,不断寻找最深的没有赋值的点,并赋值成为当前没有出现过的数中最小的

最后,对于仍然没有赋值的点,从根开始,令他们等于父亲的权值-1(注意根节点一定被赋值了)

一个很重要的结论:笛卡尔树等价于一个括号序列

于是问题转化为:求合法的括号序列,使其任何一前缀中左括号减掉右括号都小于等于$m$,的数量

这个问题我们可以利用折线法方便的解决:

一个很重要的方法:折线法可以解决括号序列问题

我们令左括号为$(+1,0)$,右括号为$(0,+1)$

那么显然问题被转化成了只在在$y=x$和$y=x+m$两条直线中间运行,最后到达$(n,n)$的不同折线的数量

这个问题中,我们可以容斥:越过一次折线以后我们就把终点关于那条折线对称一下,并乘上一个(-1)的系数

具体详见代码

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#define MOD 998244353
#define ll long long
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') flag=-1;
ch=getchar();
}
while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
inline int qpow(int a,int b){
int re=1;
while(b){
if(b&1) re=1ll*re*a%MOD;
a=1ll*a*a%MOD;b>>=1;
}
return re;
}
int n,m,ans=0,f[1000010],finv[1000010];
inline void init(){
int i,len=1000000;
f[0]=f[1]=finv[0]=finv[1]=1;
for(i=2;i<=len;i++) f[i]=1ll*f[i-1]*i%MOD;
finv[len]=qpow(f[len],MOD-2);
for(i=len;i>2;i--) finv[i-1]=1ll*finv[i]*i%MOD;
}
inline int C(int x,int y){
// cout<<"C "<<x<<' '<<y<<' '<<f[x]<<' '<<finv[y]<<' '<<finv[x-y]<<'\n';
return 1ll*f[x]*finv[y]%MOD*finv[x-y]%MOD;
}
inline void flip(int &x,int &y,int b){//关于折线翻转
int tx=x,ty=y;
x=ty+b;
y=tx-b;
}
int main(){
n=read();m=read();
if(n<m){puts("0");return 0;}
init();
ans=C(n<<1,n);
int i,x1=0,x2=0,y1=0,y2=0,d1=1,d2=-m-1;
for(i=-1;i;i=-i){
flip(x1,y1,(i>0?d1:d2));//这是两个不同的翻转方向
flip(x2,y2,(i>0?d2:d1));
if((x1>n||y1>n)&&(x2>n||y2>n)) break;
if(x1<=n&&x1>=-n) ans=((ans+i*C(n<<1,n-x1))%MOD+MOD)%MOD;
if(x2<=n&&x2>=-n) ans=((ans+i*C(n<<1,n-x2))%MOD+MOD)%MOD;
}
cout<<ans<<'\n';
}

[2018集训队作业][UOJ424] count [笛卡尔树+括号序列+折线法+组合数学]的更多相关文章

  1. 2018 Multi-University Training Contest 1 H - RMQ Similar Sequence(HDU - 6305 笛卡尔树)

    题意: 对于一个序列a,构造一个序列b,使得两个序列,对于任意的区间 [l, r] 的区间最靠近左端点的那个最大值的位置,并且序列 b 满足 0 < bi < 1. 给定一个序列 a ,求 ...

  2. [模板] 笛卡尔树 && RMQ

    话说我noip之前为什么要学这种东西... 简介 笛卡尔树(Cartesian Tree) 是一种二叉树, 且同时具有以下两种性质: 父亲节点的值大于/小于子节点的值; 中序遍历的结果为原序列. 笛卡 ...

  3. codevs2178 表达式运算Cuties[笛卡尔树]

    2178 表达式运算Cuties  时间限制: 1 s  空间限制: 32000 KB  题目等级 : 大师 Master 题解  查看运行结果     题目描述 Description 给出一个表达 ...

  4. POJ 2559 Largest Rectangle in a Histogram ——笛卡尔树

    [题目分析] 本来是单调栈的题目,用笛卡尔树可以快速的水过去. 把每一个矩阵看成一个二元组(出现的顺序,高度). 然后建造笛卡尔树. 神奇的发现,每一个节点的高度*该子树的大小,就是这一块最大的子矩阵 ...

  5. NOIP2011pj表达式的值[树形DP 笛卡尔树 | 栈 表达式解析]

    题目描述 对于1 位二进制变量定义两种运算: 运算的优先级是: 先计算括号内的,再计算括号外的. “× ”运算优先于“⊕”运算,即计算表达式时,先计算× 运算,再计算⊕运算.例如:计算表达式A⊕B × ...

  6. POJ 2201 Cartesian Tree ——笛卡尔树

    [题目分析] 构造一颗笛卡尔树,然后输出这棵树即可. 首先进行排序,然后用一个栈维护最右的树的节点信息,插入的时候按照第二关键字去找,找到之后插入,下面的树成为它的左子树即可. 然后插入分三种情况讨论 ...

  7. POJ 1785 Binary Search Heap Construction(裸笛卡尔树的构造)

    笛卡尔树: 每个节点有2个关键字key.value.从key的角度看,这是一颗二叉搜索树,每个节点的左子树的key都比它小,右子树都比它大:从value的角度看,这是一个堆. 题意:以字符串为关键字k ...

  8. [BZOJ]4199: [Noi2015]品酒大会(后缀数组+笛卡尔树)

    Time Limit: 10 Sec  Memory Limit: 512 MB Description Input Output Sample Input 10 ponoiiipoi 2 1 4 7 ...

  9. BZOJ.2616.SPOJ PERIODNI(笛卡尔树 树形DP)

    BZOJ SPOJ 直观的想法是构建笛卡尔树(每次取最小值位置划分到两边),在树上DP,这样两个儿子的子树是互不影响的. 令\(f[i][j]\)表示第\(i\)个节点,放了\(j\)个车的方案数. ...

随机推荐

  1. 安装wamp后,其显示目录的图标显示不出来

    解决办法:wamp的安装目录中,到 wamp\bin\apache\Apache2.2.21\conf \extra下打开httpd-autoindex.conf文件,这里是索引文件图标的配置文件.修 ...

  2. 韩国KT软件NB-IOT开发记录V150(2)IOT maker通信相关

    1. 测试的AT指令,创建端口和IP地址链接 AT#IMINIT=," 开始连接 AT#IMCONN 创建object ID AT#IMOBJMETA=,," 发送数据 AT#IM ...

  3. Qt-QML-自定义个自己的文本Text

    好久都没有正经的更新自己的文章了,这段时间也辞职了,听了小爱的,准备买个碗,自己当老板,下面请欣赏效果图 这个界面布局就是自己是在想不到啥了,按照常规汽车导航的布局布局了一下,主要看内容哈,看看这个文 ...

  4. 【Python+OpenCV】人脸识别基于环境Windows+Python3 version_3(Anaconda3)+OpenCV3.4.3安装配置最新版安装配置教程

    注:本次安装因为我要安装的是win10(64bit)python3.7与OpenCV3.4.3教程(当下最新版,记录下时间2018-11-17),实际中这个教程的方法对于win10,32位又或是64位 ...

  5. 总结获取原生JS(javascript)基本操作

    var a = document.getElementByIdx_x_x("dom"); jsCopy(a);//调用清理空格的函数 var b = a.childNodes;// ...

  6. 动态规划——最长上升子序列LIS及模板

    LIS定义 一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的.对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1 ...

  7. “Hello World!”团队第三周召开的第五次会议

    一.会议时间 二.会议地点 三.会议成员 四.会议内容 五.todo list 六.会议照片 七.燃尽图 八.代码地址 一.会议时间 2017年10月31日  11:45-12:17 二.会议地点: ...

  8. 2017-2018-2 20172314 『Java程序设计』课程 结对编程练习_四则运算

    相关过程截图 截图为我负责的部分关于计算的测试 关键代码解释 根据代码中的部分解释,这部分代码实现了结果的整数和分数的输出,如果算出的结果为一个真分数,就输出真分数的形式,如果结果为整数,就输出整数形 ...

  9. 20145214实验一 Java开发环境的熟悉

    20145214实验一 Java开发环境的熟悉 使用JDK编译.运行简单的java程序 命令行下程序开发 在命令行下建立20145214实验目录,进入该目录后创建exp1目录. 把代码保存到exp1目 ...

  10. 爬取CVPR 2018过程中遇到的坑

    爬取 CVPR 2018 过程中遇到的坑 使用语言及模块 语言: Python 3.6.6 模块: re requests lxml bs4 过程 一开始都挺顺利的,先获取到所有文章的链接再逐个爬取获 ...