loj 2719 「NOI2018」冒泡排序 - 组合数学
显然要考虑一个排列$p$合法的充要条件。
考虑这样一个构造$p$的过程。设排列$p^{-1}_{i}$满足$p_{p^{-1}_i} = i$。
- 初始令$q = (1, 2, \cdots, n)$。
- 依次考虑$i = 1, 2, \cdots, n$。
- 设$x = p_i$,如果$q^{-1}_x > i$,那么交换$q_x, q_{x - 1}$。
上述算法每次交换的时候会使逆序对增加1。
考虑给出的下界,假设交换的是$i$和$i + 1$。
不难用归纳法证明$p_i \leqslant i$。
那么考虑$ \Delta = (i + 1 - p_i + |p_{i + 1} - i|) - (i - p_i + |p_{i + 1} - i - 1|)$。
- 如果$p_{i + 1} \geqslant i + 1$,那么有$ \Delta = (i + 1 - p_i + p_{i + 1} - i) - (i - p_i + p_{i + 1} - i - 1) =2$
- 如果$p_{i + 1} \leqslant i$,那么有$\Delta = (i + 1 - p_i + i - p_{i + 1}) - (i - p_i + i + 1 - p_{i + 1}) = 0$
每次改变量要么为0,要么为2,如果某一次为0,那么将永远达不到下界。
因此序列合法当仅当上述算法中,每次交换满足$q_x \geqslant x$。
上述算法中,未确定的数并且可以向前移动的是一段后缀,并且满足$q_x = x$。
假如某次将$y$向前移动,那么如果一个$z < y$,并且$z$未确定,那么你不能将$z$向前移动。
然后考虑一下没有字典序限制怎么做,显然这个问题不会更难。
设$f_{i, j}$表示考虑到排列的前$i$个数,其中最大值为$j$。
转移考虑最大值有没有发生改变。
$(i, j)$是平面上的一个点,考虑把这个问题转化到平面上。
最大值改变等于可以向上走若干步,不变相当于向右走一步。
另外还需要满足$i \geqslant j$。
用折线法可以轻松计算出方案数。
然后我们来考虑原问题。
字典序严格大于似乎有点烦?考虑小于等于。(其实是我今天想的时候把题意记错了,写完发现过不了样例)
仍然考虑枚举一个长度为$i - 1$的前缀,然后计算在$i$脱离限制后的方案数。
下面只考虑长度为$i - 1$的前缀是合法的情况。
- 如果$a_{i}$是一个前缀最大值,那么考虑$i - 1$的前缀最大值是$mx$,答案加上从$(i, mx), (i, mx + 1), \cdots, (i, a_i - 1)$开始的方案数。
- 如果$a_i$不是前缀最大值
- 如果比不是前缀最大值的最小值还大,那么此时前缀$i$不合法,答案加上从$(i, mx)$开始的方案书。
- 否则对答案没有贡献。
Code
/**
* loj
* Problem#2719
* Accepted
* Time: 652ms
* Memory: 10236k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
#define ll long long void exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
} else {
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
} int inv(int a, int n) {
int x, y;
exgcd(a, n, x, y);
return (x < 0) ? (x + n) : (x);
} const int Mod = 998244353; template <const int Mod = :: Mod>
class Z {
public:
int v; Z() : v(0) { }
Z(int x) : v(x){ }
Z(ll x) : v(x % Mod) { } friend Z operator + (const Z& a, const Z& b) {
int x;
return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
}
friend Z operator - (const Z& a, const Z& b) {
int x;
return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
}
friend Z operator * (const Z& a, const Z& b) {
return Z(a.v * 1ll * b.v);
}
friend Z operator ~(const Z& a) {
return inv(a.v, Mod);
}
friend Z operator - (const Z& a) {
return Z(0) - a;
}
Z& operator += (Z b) {
return *this = *this + b;
}
Z& operator -= (Z b) {
return *this = *this - b;
}
Z& operator *= (Z b) {
return *this = *this * b;
}
friend boolean operator == (const Z& a, const Z& b) {
return a.v == b.v;
}
}; Z<> qpow(Z<> a, int p) {
Z<> rt = Z<>(1), pa = a;
for ( ; p; p >>= 1, pa = pa * pa) {
if (p & 1) {
rt = rt * pa;
}
}
return rt;
} typedef Z<> Zi; typedef class Input {
protected:
const static int limit = 65536;
FILE* file; int ss, st;
char buf[limit];
public: Input():file(NULL) { };
Input(FILE* file):file(file) { } void open(FILE *file) {
this->file = file;
} void open(const char* filename) {
file = fopen(filename, "r");
} char pick() {
if (ss == st)
st = fread(buf, 1, limit, file), ss = 0;//, cerr << "str: " << buf << "ed " << st << endl;
return buf[ss++];
}
} Input; #define digit(_x) ((_x) >= '0' && (_x) <= '9') Input& operator >> (Input& in, unsigned& u) {
char x;
while (~(x = in.pick()) && !digit(x));
for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
return in;
} Input& operator >> (Input& in, unsigned long long& u) {
char x;
while (~(x = in.pick()) && !digit(x));
for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
return in;
} Input& operator >> (Input& in, int& u) {
char x;
while (~(x = in.pick()) && !digit(x) && x != '-');
int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
u *= aflag;
return in;
} Input& operator >> (Input& in, long long& u) {
char x;
while (~(x = in.pick()) && !digit(x) && x != '-');
int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
u *= aflag;
return in;
} Input in (stdin); const int N = 6e5 + 5;
const int N2 = N << 1; int T, n;
Zi fac[N2], _fac[N2]; void init_fac(int l, int r) {
for (int i = l; i <= r; i++) {
fac[i] = fac[i - 1] * i;
}
_fac[r] = ~fac[r];
for (int i = r; i > l; i--) {
_fac[i - 1] = _fac[i] * i;
}
}
void init_fac(int n) {
static int old = 0;
fac[0] = 1, _fac[0] = 1;
if (n > old) {
init_fac(old + 1, n);
old = n;
}
}
Zi comb(int n, int m) {
return (n < m) ? (0) : (fac[n] * _fac[m] * _fac[n - m]);
} Zi C(int x, int y) {
return comb(x + y, x);
}
Zi S(int x, int y) {
if (y + 1 <= x)
return 0;
return (y == n) ? (1) : (C(n - x, n - y) - C(n + 1 - x, n - 1 - y));
} boolean vis[N];
int main() {
freopen("inverse.in", "r", stdin);
freopen("inverse.out", "w", stdout);
in >> T;
while (T--) {
in >> n;
if (!n) {
puts("0");
continue;
}
init_fac(n << 1);
memset(vis, false, n + 2);
int mx = 0, sc = 1, i = 1, a;
Zi ans = 0;
for (i = 1; i < n; i++) {
in >> a;
if (a > mx) {
for (int j = mx; j < a; j++) {
ans += S(i, j);
}
mx = a;
} else {
while (vis[sc]) sc++;
if (sc ^ a) {
ans += S(i, mx);
break;
}
}
vis[a] = true;
}
if (i == n) {
in >> a;
ans += 1;
} else {
while (++i <= n)
in >> a;
}
ans = S(0, 0) - ans;
printf("%d\n", ans.v);
}
return 0;
}
loj 2719 「NOI2018」冒泡排序 - 组合数学的更多相关文章
- Loj #2719. 「NOI2018」冒泡排序
Loj #2719. 「NOI2018」冒泡排序 题目描述 最近,小 S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 *\(1\) 到 \(n\) 的排列*的冒泡排序. 下面是对冒泡排 ...
- LOJ 2719 「NOI2018」冒泡排序——模型转化
题目:https://loj.ac/problem/2719 首先要发现合法的充要条件是 | LDS | <=2 ! 因为有没用的步数,说明一个元素先往左移.又往右移(不会先往右移再往左移,因为 ...
- LOJ #2719. 「NOI2018」冒泡排序(组合数 + 树状数组)
题意 给你一个长为 \(n\) 的排列 \(p\) ,问你有多少个等长的排列满足 字典序比 \(p\) 大 : 它进行冒泡排序所需要交换的次数可以取到下界,也就是令第 \(i\) 个数为 \(a_i\ ...
- LOJ #2721. 「NOI2018」屠龙勇士(set + exgcd)
题意 LOJ #2721. 「NOI2018」屠龙勇士 题解 首先假设每条龙都可以打死,每次拿到的剑攻击力为 \(ATK\) . 这个需要支持每次插入一个数,查找比一个 \(\le\) 数最大的数(或 ...
- loj#2718. 「NOI2018」归程
题目链接 loj#2718. 「NOI2018」归程 题解 按照高度做克鲁斯卡尔重构树 那么对于询问倍增找到当前点能到达的高度最小可行点,该点的子树就是能到达的联通快,维护子树中到1节点的最短距离 s ...
- loj#2721. 「NOI2018」屠龙勇士
题目链接 loj#2721. 「NOI2018」屠龙勇士 题解 首先可以列出线性方程组 方程组转化为在模p意义下的同余方程 因为不保证pp 互素,考虑扩展中国剩余定理合并 方程组是带系数的,我们要做的 ...
- LOJ2719 「NOI2018」冒泡排序
「NOI2018」冒泡排序 题目描述 最近,小S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 1 到n 的排列的冒泡排序. 下面是对冒泡排序的算法描述. 输入:一个长度为n 的排列p[ ...
- 「NOI2018」冒泡排序
「NOI2018」冒泡排序 考虑冒泡排序中一个位置上的数向左移动的步数 \(Lstep\) 为左边比它大的数的个数,向右移动的步数 \(Rstep\) 为右边比它大的数的个数,如果 \(Lstep,R ...
- loj#2720. 「NOI2018」你的名字
链接大合集: loj uoj luogu bzoj 单纯地纪念一下写的第一份5K代码.../躺尸 因为ZJOI都不会所以只好写NOI的题了... 总之字符串题肯定一上来就拼个大字符串跑后缀数组啦! ( ...
随机推荐
- python Lock、RLock
Lock: 只能acquire一次,下一次acquire必须release后才能,不然会造成死锁 from threading import Lock total = 0 lock = Lock() ...
- LeetCode 1290. Convert Binary Number in a Linked List to Integer
题目 /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListN ...
- C++类型处理:typedef decltype
类型别名 类型别名是某种类型的同义词 有两种方法用于定义类型别名,传统方法是使用typedef: typedef double wages; //wages是double的同义词 typedef wa ...
- WPF 动态资源 DataContext="{DynamicResource studentListKey}" DisplayMemberPath="Name"
public class StudentList:ObservableCollection<Student> { public List<Student> studentLis ...
- WEBAPI 设置上传文件大小
参考资料:https://stackoverflow.com/questions/33399267/cors-error-when-uploading-larger-files https:// ...
- 使用T4模板同时生成多个类文件
代码: <#@ template language="C#" debug="false" hostspecific="true"#&g ...
- java都是值传递,没有引用传递
博主这几天在复习 javaSE 部分的内容时,遇到了关于参数传值的问题,但是始终不知道原因,上网上一查才知道钻牛角尖了,把C语言的参数传值转移到java中了. 相信很多在学习java之前,有接触过C/ ...
- 【设计模式】Composite
目录 前言 安卓View的实现 View Beyond setContentView setContentView做了什么事情? 如何将xml文件变成对象的? 小结 View的绘制流程 三个流程 三个 ...
- Class文件和JVM的恩怨情仇
类的加载时机 现在我们例子中生成的两个.class文件都会直接被加载到JVM中吗?? 虚拟机规范则是严格规定了有且只有5种情况必须立即对类进行“初始化”(class文件加载到JVM中): 创建类的实例 ...
- golang多个项目时如何配置GOPATH,使用gb包依赖管理工具,不同项目配置不同的GOPATH的
golang多个项目时如何配置GOPATH,使用gb包依赖管理工具,不同项目配置不同的GOPATH的 1:执行脚本setGoPath.sh#!/bin/bashif [[ $GOPATH =~ .*$ ...