Codeforces Round 936 E. Girl Permutation(分治、组合计数)

2024-03-29 20:44

本文主要是介绍Codeforces Round 936 E. Girl Permutation(分治、组合计数),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

E. Girl Permutation

1
2

题意

有一个位置的长度为 n n n排列 ,现在给定一个前缀最值下标数组 p p p 和一个后缀最值下标数组 s s s

  • 在位置 i i i 的前缀最值下标定义为:以 i i i结尾的前缀,最大值恰好在 i i i,也就是 ∀ j < i , a j < a i \forall j < i,a_j < a_i j<iaj<ai,如果不满足条件,那么 i i i 不出现在 p p p

后缀最值下标也是类似

现在求出符合 p p p s s s 的排列个数,对 1 e 9 + 7 1e9 + 7 1e9+7 求模

思路

首先不难观察到:最大值 n n n 所在的下标一定会出现在 p [ m 1 − 1 ] p[m1-1] p[m11] s [ 0 ] s[0] s[0],并且 p [ 0 ] = 1 p[0] = 1 p[0]=1 s [ m 2 − 1 ] = n s[m2 - 1] = n s[m21]=n,如果不满足这些条件,就不存在任何一个满足条件的排列

否则,我们可以唯一确定 n n n 的位置,就是在 p [ m 1 − 1 ] p[m1 - 1] p[m11],那么我们需要从 n − 1 n - 1 n1(最大值 n n n 已被分配)个数中,选择 p [ m 1 − 1 ] − 1 p[m1 - 1] - 1 p[m11]1 个数到左半部分,剩下的数在右半部分,这里的方案数是 ( n − 1 p m 1 − 1 − 1 ) \binom{n - 1}{p_{m_1-1} - 1} (pm111n1),然后我们继续对于 p [ m 1 − 2 ] p[m1-2] p[m12] 考虑,可以发现,这个位置是 [ 1 , p m 1 − 1 − 1 ] [1,p_{m_1-1}-1] [1,pm111] 这个前缀的最大值,也就是我们前面分配过来的那些数的最大值!

想到这里,我们就可以考虑分治来求解方案数了,对于当前的一个左半部份,其最大值在 p [ i ] p[i] p[i],那么表示的是前缀 [ 1 , p [ i + 1 ] − 1 ] [1, p[i + 1] - 1] [1,p[i+1]1] 这个子状态,我们继续从 p [ i + 1 ] − 2 p[i + 1] - 2 p[i+1]2 个数(两个数已经在 p [ i ] p[i] p[i] p [ i + 1 ] p[i + 1] p[i+1])中选择 p [ i ] − 1 p[i] - 1 p[i]1 个数到 p [ i ] p[i] p[i] 的左边,其余留在 [ p [ i ] + 1 , p [ i + 1 ] − 1 ] [p[i] + 1, p[i + 1] - 1] [p[i]+1,p[i+1]1],这里贡献的方案数是 ( p i + 1 − 2 p i − 1 ) \binom{p_{i + 1} - 2}{p_i - 1} (pi1pi+12),注意这里还要考虑留在 [ p [ i ] + 1 , p [ i + 1 ] − 1 ] [p[i] + 1, p[i + 1] - 1] [p[i]+1,p[i+1]1] 的部分,这里的数字可以 任意排列 ,因此我们要乘上阶乘 ( p i + 1 − p i − 1 ) ! (p_{i + 1} - p_i - 1)! (pi+1pi1)!

对于右半部分,也是类似的计算方法

#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;const int INF=0x3f3f3f3f;
const long long INFLL=1e18;typedef long long ll;template<class T>
constexpr T power(T a, ll b){T res = 1;while(b){if(b&1) res = res * a;a = a * a;b >>= 1;}return res;
}constexpr ll mul(ll a,ll b,ll mod){ //快速乘,避免两个long long相乘取模溢出ll res = a * b - ll(1.L * a * b / mod) * mod;res %= mod;if(res < 0) res += mod; //误差return res;
}template<ll P>
struct MLL{ll x;constexpr MLL() = default;constexpr MLL(ll x) : x(norm(x % getMod())) {}static ll Mod;constexpr static ll getMod(){if(P > 0) return P;return Mod;}constexpr static void setMod(int _Mod){Mod = _Mod;}constexpr ll norm(ll x) const{if(x < 0){x += getMod();}if(x >= getMod()){x -= getMod();}return x;}constexpr ll val() const{return x;}explicit constexpr operator ll() const{ return x; //将结构体显示转换为ll类型: ll res = static_cast<ll>(OBJ)}constexpr MLL operator -() const{ //负号,等价于加上ModMLL res;res.x = norm(getMod() - x);return res;}constexpr MLL inv() const{assert(x != 0);return power(*this, getMod() - 2); //用费马小定理求逆}constexpr MLL& operator *= (MLL rhs) & { //& 表示“this”指针不能指向一个临时对象或const对象x = mul(x, rhs.x, getMod()); //该函数只能被一个左值调用return *this;}constexpr MLL& operator += (MLL rhs) & {x = norm(x + rhs.x);return *this;}constexpr MLL& operator -= (MLL rhs) & {x = norm(x - rhs.x);return *this;}constexpr MLL& operator /= (MLL rhs) & {return *this *= rhs.inv();}friend constexpr MLL operator * (MLL lhs, MLL rhs){MLL res = lhs;res *= rhs;return res;}friend constexpr MLL operator + (MLL lhs, MLL rhs){MLL res = lhs;res += rhs;return res;}friend constexpr MLL operator - (MLL lhs, MLL rhs){MLL res = lhs;res -= rhs;return res;}friend constexpr MLL operator / (MLL lhs, MLL rhs){MLL res = lhs;res /= rhs;return res;}friend constexpr std::istream& operator >> (std::istream& is, MLL& a){ll v;is >> v;a = MLL(v);return is;}friend constexpr std::ostream& operator << (std::ostream& os, MLL& a){return os << a.val();}friend constexpr bool operator == (MLL lhs, MLL rhs){return lhs.val() == rhs.val();}friend constexpr bool operator != (MLL lhs, MLL rhs){return lhs.val() != rhs.val();}
};const ll mod = 1e9 + 7;
using Z = MLL<mod>;struct Comb {int n;std::vector<Z> _fac;std::vector<Z> _invfac;std::vector<Z> _inv;Comb() : n{0}, _fac{1}, _invfac{1}, _inv{0} {}Comb(int n) : Comb() {init(n);}void init(int m) {m = std::min(1ll * m, Z::getMod() - 1);if (m <= n) return; //已经处理完了需要的长度_fac.resize(m + 1);_invfac.resize(m + 1);_inv.resize(m + 1);for (int i = n + 1; i <= m; i++) {_fac[i] = _fac[i - 1] * i;}_invfac[m] = _fac[m].inv();for (int i = m; i > n; i--) { //线性递推逆元和阶乘逆元_invfac[i - 1] = _invfac[i] * i;_inv[i] = _invfac[i] * _fac[i - 1];}n = m; //新的长度}Z fac(int m) {if (m > n) init(2 * m);return _fac[m];}Z invfac(int m) {if (m > n) init(2 * m);return _invfac[m];}Z inv(int m) {if (m > n) init(2 * m);return _inv[m];}Z binom(int n, int m) { //二项式系数if (n < m || m < 0) return 0;return fac(n) * invfac(m) * invfac(n - m);}
} comb;int main(){std::ios::sync_with_stdio(false);std::cin.tie(nullptr);std::cout.tie(nullptr);int t;std::cin >> t;while(t--) {int n, m1, m2;std::cin >> n >> m1 >> m2;std::vector<int> p(m1), s(m2);fore(i, 0, m1) std::cin >> p[i];fore(i, 0, m2) std::cin >> s[i];if(p[0] != 1 || p[m1 - 1] != s[0] || s[m2 - 1] != n){std::cout << "0\n";continue;}Z ans = comb.binom(n - 1, p[m1 - 1] - 1);for(int i = m1 - 2; i >= 0; --i) ans *= comb.binom(p[i + 1] - 2, p[i] - 1) * comb.fac(p[i + 1] - p[i] - 1);fore(i, 1, m2) ans *= comb.binom(n - s[i - 1] - 1, n - s[i]) * comb.fac(s[i] - s[i - 1] - 1);std::cout << ans << endl;}return 0;
}

这篇关于Codeforces Round 936 E. Girl Permutation(分治、组合计数)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/859598

相关文章

hdu4869(逆元+求组合数)

//输入n,m,n表示翻牌的次数,m表示牌的数目,求经过n次操作后共有几种状态#include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#includ

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

Codeforces Round #261 (Div. 2)小记

A  XX注意最后输出满足条件,我也不知道为什么写的这么长。 #define X first#define Y secondvector<pair<int , int> > a ;int can(pair<int , int> c){return -1000 <= c.X && c.X <= 1000&& -1000 <= c.Y && c.Y <= 1000 ;}int m

Codeforces Beta Round #47 C凸包 (最终写法)

题意慢慢看。 typedef long long LL ;int cmp(double x){if(fabs(x) < 1e-8) return 0 ;return x > 0 ? 1 : -1 ;}struct point{double x , y ;point(){}point(double _x , double _y):x(_x) , y(_y){}point op

Codeforces Round #113 (Div. 2) B 判断多边形是否在凸包内

题目点击打开链接 凸多边形A, 多边形B, 判断B是否严格在A内。  注意AB有重点 。  将A,B上的点合在一起求凸包,如果凸包上的点是B的某个点,则B肯定不在A内。 或者说B上的某点在凸包的边上则也说明B不严格在A里面。 这个处理有个巧妙的方法,只需在求凸包的时候, <=  改成< 也就是说凸包一条边上的所有点都重复点都记录在凸包里面了。 另外不能去重点。 int

Codeforces 482B 线段树

求是否存在这样的n个数; m次操作,每次操作就是三个数 l ,r,val          a[l] & a[l+1] &......&a[r] = val 就是区间l---r上的与的值为val 。 也就是意味着区间[L , R] 每个数要执行 | val 操作  最后判断  a[l] & a[l+1] &......&a[r] 是否= val import ja

Go组合

摘要 golang并非完全面向对象的程序语言,为了实现面向对象的继承这一神奇的功能,golang允许struct间使用匿名引入的方式实现对象属性方法的组合 组合使用注意项 使用匿名引入的方式来组合其他struct 默认优先调用外层方法 可以指定匿名struct以调用内层方法 代码 package mainimport ("fmt")type People struct{}type Pe

YOLOv8/v10+DeepSORT多目标车辆跟踪(车辆检测/跟踪/车辆计数/测速/禁停区域/绘制进出线/绘制禁停区域/车道车辆统计)

01:YOLOv8 + DeepSort 车辆跟踪 该项目利用YOLOv8作为目标检测模型,DeepSort用于多目标跟踪。YOLOv8负责从视频帧中检测出车辆的位置,而DeepSort则负责关联这些检测结果,从而实现车辆的持续跟踪。这种组合使得系统能够在视频流中准确地识别并跟随特定车辆。 02:YOLOv8 + DeepSort 车辆跟踪 + 任意绘制进出线 在此基础上增加了用户

组合c(m,n)的计算方法

问题:求解组合数C(n,m),即从n个相同物品中取出m个的方案数,由于结果可能非常大,对结果模10007即可。       共四种方案。ps:注意使用限制。 方案1: 暴力求解,C(n,m)=n*(n-1)*...*(n-m+1)/m!,n<=15 ; int Combination(int n, int m) { const int M = 10007; int

代码随想录训练营day37|52. 携带研究材料,518.零钱兑换II,377. 组合总和 Ⅳ,70. 爬楼梯

52. 携带研究材料 这是一个完全背包问题,就是每个物品可以无限放。 在一维滚动数组的时候规定了遍历顺序是要从后往前的,就是因为不能多次放物体。 所以这里能多次放物体只需要把遍历顺序改改就好了 # include<iostream># include<vector>using namespace std;int main(){int n,m;cin>>n>>m;std::vector<i