LOJ

思路

这题我看着题解还搞了几个小时?我也不知道自己在干啥……

首先你要通过出色的分析能力得到一个结论:一个排列合法当且仅当它的最长下降子序列长度不超过2。

证明?懒得写了。

然后我们不管字典序的限制,先写出一个DP:\(dp_{i,j}\)表示考虑了前\(i\)个,之前最大值是\(j\),的方案数。转移就考虑下一个位置是填一个比最大值更大的数,或是填还没有填的数里面最小的数。

其实就是\(dp_{i,j}\rightarrow dp_{i+1,k},k\ge j\),但\(i=j\)的时候不能转移到\(dp_{i+1,j}\)。

我们假装\(dp_{i+1,j}\)这里也有一个虚点,那么就可以把转移看成往右走一步,然后往上走若干步。这里的转移可以转移到\(dp_{i+1,i}\),但是不能把\(dp_{i+1,i}\)转移到其他位置。(其实就是为了方便才搞出这么个特殊点)

初始从\(dp_{0,0}=1\)开始走,第一步必须向右然后向上若干步。最后答案是\(dp_{n,n}\)。

观察这个DP式子,发现把\(dp_{i,j}\)看做点\((i,j)\),那么它的值就是从\((1,1)\)走到\((i,j)\),只能向右或向上,并且不能摸到\(y=x-2\)这一条直线,的方案数。

不能碰到某条直线的这个套路我们非常熟悉,就是把起点对于它对称一下,方案数减掉,就可以了。

于是我们有\(dp_{n,n}={2n-2\choose n-1}-{2n-2\choose n-3}\)。

那么现在再来看一看字典序的限制,容易想到枚举前面几项相等,有一项更大,然后后面放飞自我。

这个怎么处理呢?设第\(i\)位不同,已经填完的数的最大值为\(mx\),那么这一位就必须填大于\(\max({mx,a_i})\)的数。为什么?如果小于\(mx\),那么就必须是未出现里面最小的数,就一定小于等于\(a_i\)了,就不合法了。

这相当于限定起点是\((i,\max(a_i,mx)+1)\),仍然是走到\((n,n)\),所以方案数也很容易算。

还有一个注意的地方是前\(i-1\)个的填法必须合法,也就是说每次要么大于最大值要么是最小值,否则就立刻break

代码

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
using namespace std;
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define drep(i,x,y) for (int i=(x);i>=(y);i--)
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define templ template<typename T>
#define sz 1206060
#define mod 998244353ll
typedef long long ll;
typedef double db;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
templ inline void read(T& t)
{
t=0;char f=0,ch=getchar();double d=0.1;
while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
t=(f?-t:t);
}
template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
char __sr[1<<21],__z[20];int __C=-1,__zz=0;
inline void Ot(){fwrite(__sr,1,__C+1,stdout),__C=-1;}
inline void print(register int x)
{
if(__C>1<<20)Ot();if(x<0)__sr[++__C]='-',x=-x;
while(__z[++__zz]=x%10+48,x/=10);
while(__sr[++__C]=__z[__zz],--__zz);__sr[++__C]='\n';
}
void file()
{
#ifdef NTFOrz
freopen("a.in","r",stdin);
#else
freopen("inverse.in","r",stdin);
freopen("inverse.out","w",stdout);
#endif
}
inline void chktime()
{
#ifndef ONLINE_JUDGE
cout<<(clock()-t)/1000.0<<'\n';
#endif
}
#ifdef mod
ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
ll inv(ll x){return ksm(x,mod-2);}
#else
ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
#endif
// inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std; int n;
int a[sz]; ll fac[sz],_fac[sz];
void init(){_fac[0]=fac[0]=1;rep(i,1,sz-1) fac[i]=fac[i-1]*i%mod;_fac[sz-1]=inv(fac[sz-1]);drep(i,sz-2,1) _fac[i]=_fac[i+1]*(i+1)%mod;}
ll C(int n,int m){return n>=m&&m>=0?fac[n]*_fac[m]%mod*_fac[n-m]%mod:0;} ll calc(int x,int y)
{
ll ret=C(n-x+n-y,n-x);
x-=1,y+=1,swap(x,y),x+=1,y-=1;
ret=(mod+ret-C(n-x+n-y,n-x))%mod;
return ret;
} int vis[sz]; void work()
{
read(n);
rep(i,1,n) read(a[i]);
int mx=0,mn=1;ll ans=0;
rep(i,1,n)
{
(ans+=calc(i,max(a[i],mx)+1))%=mod;
if (a[i]<mx&&a[i]!=mn) break;
vis[a[i]]=1;
while (vis[mn]) ++mn;
chkmax(mx,a[i]);
}
printf("%lld\n",ans);
rep(i,1,n) vis[i]=0;
} int main()
{
file();
init();
int T;read(T);
while (T--) work();
return 0;
}

LOJ2719. 「NOI2018」冒泡排序 [组合计数]的更多相关文章

  1. LOJ2719 「NOI2018」冒泡排序

    「NOI2018」冒泡排序 题目描述 最近,小S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 1 到n 的排列的冒泡排序. 下面是对冒泡排序的算法描述. 输入:一个长度为n 的排列p[ ...

  2. Loj #2719. 「NOI2018」冒泡排序

    Loj #2719. 「NOI2018」冒泡排序 题目描述 最近,小 S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 *\(1\) 到 \(n\) 的排列*的冒泡排序. 下面是对冒泡排 ...

  3. 「NOI2018」冒泡排序

    「NOI2018」冒泡排序 考虑冒泡排序中一个位置上的数向左移动的步数 \(Lstep\) 为左边比它大的数的个数,向右移动的步数 \(Rstep\) 为右边比它大的数的个数,如果 \(Lstep,R ...

  4. LOJ #2719. 「NOI2018」冒泡排序(组合数 + 树状数组)

    题意 给你一个长为 \(n\) 的排列 \(p\) ,问你有多少个等长的排列满足 字典序比 \(p\) 大 : 它进行冒泡排序所需要交换的次数可以取到下界,也就是令第 \(i\) 个数为 \(a_i\ ...

  5. loj 2719 「NOI2018」冒泡排序 - 组合数学

    题目传送门 传送门 题目大意 (相信大家都知道) 显然要考虑一个排列$p$合法的充要条件. 考虑这样一个构造$p$的过程.设排列$p^{-1}_{i}$满足$p_{p^{-1}_i} = i$. 初始 ...

  6. LOJ 2719 「NOI2018」冒泡排序——模型转化

    题目:https://loj.ac/problem/2719 首先要发现合法的充要条件是 | LDS | <=2 ! 因为有没用的步数,说明一个元素先往左移.又往右移(不会先往右移再往左移,因为 ...

  7. LOJ2722 「NOI2018」情报中心

    「NOI2018」情报中心 题目描述 C 国和D 国近年来战火纷飞. 最近,C 国成功地渗透进入了D 国的一个城市.这个城市可以抽象成一张有$n$ 个节点,节点之间由$n - 1$ 条双向的边连接的无 ...

  8. 「NOI2013」树的计数 解题报告

    「NOI2013」树的计数 这什么神题 考虑对bfs重新编号为1,2,3...n,然后重新搞一下dfs序 设dfs序为\(dfn_i\),dfs序第\(i\)位对应的节点为\(pos_i\) 一个暴力 ...

  9. 「NOI2018」屠龙勇士(EXCRT)

    「NOI2018」屠龙勇士(EXCRT) 终于把传说中 \(NOI2018D2\) 的签到题写掉了... 开始我还没读懂题目...而且这题细节巨麻烦...(可能对我而言) 首先我们要转换一下,每次的 ...

随机推荐

  1. 第三章:JavaScript选择元素

    我们使用jQuery时,很常用的套路是“两步”第一步:选取元素第二步:对选中的元素执行需要的操作这一章我们重点研究第一步,如何使用jQuery选取元素以及对选取的结果进行“各种筛选”以满足我们的需求. ...

  2. Twitter分布式自增ID算法snowflake原理解析(Long类型)

    Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1). 那么一个Long类型的6 ...

  3. maven cmd 命令

    1. mvn clean install :重新清理打包 2.详见:https://www.cnblogs.com/lukelook/p/11298168.html mvn  versions:upd ...

  4. centos 7.6 安装php70

    1.首先查看是否有老版本 yum list installed | grep php 2.如果安装的有,清除老版本 yum remove php.x86_64 php-cli.x86_64 php-c ...

  5. SQL SERVER-Alwayson原理

    流程 1.异步提交模式 主副本无须确认该副本已经完成日志固化,就可提交事务. 主副本不受辅助副本的影响 辅助副本上的DB处于SYNCHRONIZING 2.同步提交模式 主副本要确认副本已经完成日志固 ...

  6. Burp Suite Extension tools

    1.Setting up the envrionment for burp Extensions   before we can write extensions we need to ensure ...

  7. unity 之 场景切换进度条显示

    一.UI.建立slider适当更改即可: 二.新增loadScene脚本,用来进行场景切换,将其绑定任意物体上面.博主以放置主相机为例.参数分别为进度条(用来设置value值),显示进度文本text: ...

  8. mysql 数据库的时间与字符串转换

    .当前日期.时间 now() 获取 当前日期和时间 :: curdate() 当前日期, curtime() 当前时间 :: current_time() : //同curtime(),current ...

  9. python 私有和保护成员变量如何实现?—— "单下划线 " 开始的成员变量叫做保护变量,意思是只有类实例和子类实例能访问到这些变量;" 双下划线 " 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据

    默认情况下,Python中的成员函数和成员变量都是公开的(public),在python中没有类似public,private等关键词来修饰成员函数和成员变量.在python中定义私有变量只需要在变量 ...

  10. Java精通并发-透过字节码理解synchronized关键字

    在上一次https://www.cnblogs.com/webor2006/p/11428408.html中对于synchronized关键字的作用做了一个实例详解,下面再来看一下这个程序: 请问下, ...