( ext{Bottom-Tier Reversals})
解法
首先可以观察得到 reverse
操作并不会改变数字所在下标的奇偶性,这可以判掉无解。
如果我们将 (n-1,n) 排到正确的位置就不会再改变它的位置,这相当于将原问题缩减到 (n-2)。所以我们可以先考虑如何排这两个数。需要注意的是,(n-1) 在偶数位置,(n) 在奇数位置。
不妨倒着构造。首先需要达到 "(n) 在 (1) 位置,(n-1) 在 (2) 位置" 的状态,由此可知肯定还存在一个 "(n) 在 (i+1) 位置,(n-1) 在 (i) 位置" 的状态,而这个状态可以由 "(n) 在 (j-1) 位置,(n-1) 在 (j) 位置" 得到,只要我们翻一个 (>j) 的位置就行了。于是不妨将 (n) 先翻到 (1),设此时 (n-1) 的位置为 (p),将 (n) 翻到 (p-1),就可以倒着构造一组解。一共需要 (5) 次。
所以总次数是 (frac{5(n-1)}{2})。
( ext{Top-Notch Insertions})
题目大意
对长度为 (n) 的序列 (a) 进行插入排序,其中每个元素都在区间 ([1,n]) 之间,元素可以相等。从小到大枚举每个位置 (i),如果有 (a_{i-1}>a_i),就找到 第一个 位置 (j) 满足 (a_i<a_j) 并把 (i) 插入到 (j) 的前面,并把其记为操作 ((i,j))。
给出长度为 (m) 的操作序列 (p),请算出有多少个序列 (a) 的插入排序的操作序列为 (p)。
多组数据,(n,sum mle 2cdot 10^5)。
解法
对于固定的操作序列,原序列和终序列是一一对应的。问题转化为统计合法终序列的个数。
不妨设终序列为 (b)。那么它肯定满足 (forall iin[1,n),b_ile b_{i+1})。
但事实上操作序列要求某些关系是 <
的。考虑对于固定的操作序列,终序列中每个符号的位置都是固定的!所以统计个数就转化成了一个组合问题。终序列中有 (n-1) 个符号,设有 (c) 个 <
,那么方案数可以表示成 (inom{2n-c-1}{n})。具体证明就是对于每个 (b_ile b_{i+1}),将 ([i+1,n]) 中的 (b) 全部加一。这样的好处就是所有数都互不相同,而且相对关系不会发生变化。由于初始每个元素都在区间 ([1,n]) 之间,所以此时元素的最大值就是 (n+(n-1-c))。因为排列顺序已经确定,所以只用从 ([1,2n-1-c]) 中选择 (n) 个数即可。倒推回原来的序列可以用相邻两项相差 (1)。
如何求解 (c)?我们维护一个 集合 存储有哪些数 "被插" 了,答案就是集合大小(一个数可能多次 "被插",这也是 (m) 可能不等于 (c) 的原因)。可以写一个平衡树,但实际上可以倒着维护,写一个线段树二分。具体而言,对于每个操作 ((x,y)),找到当前第 (y) 个数与第 (y+1) 个数,第 (y) 个数就是这个操作插入的数,直接在线段树上删掉它。因为操作序列的 (x) 是递增的,所以不将第 (y) 个数插回去也不会影响之前的插入。
不过需要注意复杂度要写成 (mathcal O(mlog n))。
代码
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while((s=getchar())>'9' or s<'0')
f|=(s=='-');
while(s>='0' and s<='9')
x=(x<<1)+(x<<3)+(s^48),
s=getchar();
return f?-x:x;
}
template <class T>
inline void write(const T x) {
if(x<0) {
putchar('-'),write(-x);
return;
}
if(x>9) write(x/10);
putchar(x%10^48);
}
#include <set>
using namespace std;
const int maxn=2e5+5,mod=998244353;
int n,m,x[maxn],y[maxn],roll[maxn];
int fac[maxn<<1],ifac[maxn<<1];
set <int> ans;
struct SgTree {
int t[maxn<<2];
void build(int o,int l,int r) {
t[o]=r-l+1;
if(l==r) return;
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
}
int ask(int o,int l,int r,int k) {
while(233) {
int mid=l+r>>1;
if(l==r) return l;
if(k<=t[o<<1])
o<<=1,r=mid;
else {
k-=t[o<<1];
o=o<<1|1; l=mid+1;
}
}
}
void modify(int o,int l,int r,int p,int k) {
if(l==r) return (void)(t[o]+=k);
int mid=l+r>>1;
if(p<=mid) modify(o<<1,l,mid,p,k);
else modify(o<<1|1,mid+1,r,p,k);
t[o]=t[o<<1]+t[o<<1|1];
}
} T;
int inv(int x,int y=mod-2) {
int r=1;
while(y) {
if(y&1) r=1ll*r*x%mod;
x=1ll*x*x%mod; y>>=1;
}
return r;
}
void init() {
fac[0]=1;
for(int i=1;i<=(int)4e5;++i)
fac[i]=1ll*fac[i-1]*i%mod;
ifac[(int)4e5]=inv(fac[(int)4e5]);
for(int i=(int)4e5-1;i>=0;--i)
ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
T.build(1,1,maxn-5);
}
int C(int n,int m) {
if(n<m or n<0 or m<0) return 0;
return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int main() {
init();
for(int t=read(9);t;--t) {
n=read(9),m=read(9);
ans.clear();
for(int i=1;i<=m;++i)
x[i]=read(9),y[i]=read(9);
for(int i=m;i>=1;--i) {
int u=T.ask(1,1,maxn-5,y[i]);
int v=T.ask(1,1,maxn-5,y[i]+1);
T.modify(1,1,maxn-5,u,-1);
ans.insert(v);
roll[i]=u;
}
for(int i=1;i<=m;++i)
T.modify(1,1,maxn-5,roll[i],1);
int p=ans.size();
print(C(2*n-p-1,n),'
');
}
return 0;
}