「Positions in Permutations」Solution

2024-04-11 08:36

本文主要是介绍「Positions in Permutations」Solution,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简述题意

给定 n , m n,m n,m,对于一个长度为 n n n 的排列 p p p,有函数: F ( p ) = ∑ i = 1 n [ ∣ p i − i ∣ = 1 ] F(p)=\sum_{i=1}^{n}[\left|p_i-i\right|=1 ] F(p)=i=1n[pii=1]求有多少个排列满足 F ( p ) = m F(p) = m F(p)=m,答案对 1 0 9 + 7 10^9+7 109+7 取模。

  • 1 ≤ m ≤ n ≤ 1 0 3 1\le m \le n \le 10^3 1mn103

思路

看到这类 “恰好等于” 的问题,很自然想到二项式反演。不妨令 f ( i ) f(i) f(i) F ( p ) ≥ i F(p) \ge i F(p)i 的方案数,令 g ( i ) g(i) g(i) F ( p ) = i F(p) = i F(p)=i 的方案数,根据二项式反演得:

g ( m ) = ∑ i = m n f ( i ) × C m i × − 1 i − m g(m)=\sum_{i=m}^{n}f(i) \times C_m^i \times -1^{i-m} g(m)=i=mnf(i)×Cmi×1im

考虑如何求出 f ( i ) f(i) f(i)。注意到此题, F ( p ) F(p) F(p) 实质是在统计有多少 i i i 满足: p i = i + 1 p_i=i+1 pi=i+1 p i = i − 1 p_i=i-1 pi=i1。考虑去除排列的限制,即可以使用任意数任意次,那么显然有 f ( i ) = C n i × ( n − i ) ! × 2 i f(i)=C^i_n \times (n-i)! \times 2^i f(i)=Cni×(ni)!×2i。如果我们加上排列的限制,观察上述式子中的哪些情况变得不合法:

  • p 1 = 0 p_1 = 0 p1=0
  • p n = n + 1 p_n=n+1 pn=n+1
  • p i − 1 = i p_{i-1}=i pi1=i p i + 1 = i p_{i+1}=i pi+1=i

难点便在于该如何规避掉上述不合法的情况。假设我们钦定满足条件的 i i i 组成的集合 S S S,那么我们只关心集合中的 i i i 位置的取值,剩下未在集合中的位置(即有无贡献皆可)随意乱填即可。

注意到:如果我们处理到第 i i i 个位置,且 i i i 在我们钦定的集合 S S S 里,那么我们只关心 i − 1 i-1 i1 i + 1 i+1 i+1 这两个数在之前是否被选取,因为我们只能在 i i i 位置上填这两个数。即,当前被使用掉的数,一定是某个 i i i 加上 1 1 1 或减去 1 1 1 的结果。如果当前位置填 i − 1 i-1 i1,那么 i + 1 i+1 i+1 在之前一定未出现过,这启发我们 DP \text{DP} DP

不妨令 d p i , j , 0 / 1 , 0 / 1 dp_{i,j,0/1,0/1} dpi,j,0/1,0/1 表示处理到第 i i i 个位置,钦定 j j j 个位置满足条件,且 i i i 是/否 被选取, i + 1 i+1 i+1 是/否 被选取的方案数。
注意:我们肯定不会定义 i − 1 i-1 i1 是/否 出现, i + 1 i+1 i+1 是/否 出现这样的状态,否则会产生状态上的冲突,而且会漏掉很多情况。
考虑分类转移:

  • 当前位置填 i − 1 i-1 i1,那么 i + 1 i+1 i+1 一定未被选取。
    d p i , j , 0 , 0 = d p i − 1 , j − 1 , 0 , 0 dp_{i,j,0,0}=dp_{i-1,j-1,0,0} dpi,j,0,0=dpi1,j1,0,0 d p i , j , 1 , 0 = d p i − 1 , j − 1 , 0 , 1 dp_{i,j,1,0}=dp_{i-1,j-1,0,1} dpi,j,1,0=dpi1,j1,0,1
  • 当前位置填 i + 1 i+1 i+1 i − 1 i-1 i1 有可能被选取。
    d p i , j , 0 , 1 = d p i − 1 , j − 1 , 0 , 0 + d p i − 1 , j − 1 , 1 , 0 dp_{i,j,0,1}=dp_{i-1,j-1,0,0}+dp_{i-1,j-1,1,0} dpi,j,0,1=dpi1,j1,0,0+dpi1,j1,1,0 d p i , j , 1 , 1 = d p i − 1 , j − 1 , 0 , 1 + d p i − 1 , j − 1 , 1 , 1 dp_{i,j,1,1}=dp_{i-1,j-1,0,1}+dp_{i-1,j-1,1,1} dpi,j,1,1=dpi1,j1,0,1+dpi1,j1,1,1
  • 当前位置不作贡献。
    d p i , j , 0 , 0 = d p i − 1 , j , 0 , 0 + d p i − 1 , j , 1 , 0 dp_{i,j,0,0}=dp_{i-1,j,0,0}+dp_{i-1,j,1,0} dpi,j,0,0=dpi1,j,0,0+dpi1,j,1,0 d p i , j , 1 , 0 = d p i − 1 , j , 0 , 1 + d p i − 1 , j , 1 , 1 dp_{i,j,1,0}=dp_{i-1,j,0,1}+dp_{i-1,j,1,1} dpi,j,1,0=dpi1,j,0,1+dpi1,j,1,1

注意边界条件: d p 1 , 0 , 0 , 0 = d p 1 , 1 , 0 , 1 = 1 dp_{1,0,0,0}=dp_{1,1,0,1}=1 dp1,0,0,0=dp1,1,0,1=1

暴力 O ( n 2 ) O(n^2) O(n2) 转移得到 d p dp dp 值,然后有:
f ( i ) = ( d p n , i , 0 , 0 + d p n , i , 1 , 0 ) × ( n − i ) ! f(i)=(dp_{n,i,0,0}+dp_{n,i,1,0}) \times (n-i)! f(i)=(dpn,i,0,0+dpn,i,1,0)×(ni)!

得出 f ( i ) f(i) f(i) 的值以后,套用二项式反演即可。

代码

#include<bits/stdc++.h>
#define int long long
const int MAXN = 1e3 + 5 , MOD = 1e9 + 7;
using namespace std;
int n , m , dp[MAXN][MAXN][2][2] , ans , jc[MAXN];
int qpow(int base , int k) {int res = 1;while(k) {if (k & 1) res = res * base % MOD;base = base * base % MOD;k >>= 1;}return res;
}
int inv(int x) {return qpow(x , MOD - 2);};
int C(int x , int y) {return jc[x] * inv(jc[y]) % MOD * inv(jc[x - y]) % MOD;}
signed main(){ios::sync_with_stdio(false);cin.tie(nullptr) , cout.tie(nullptr);cin >> n >> m;jc[0] = 1;for (int i = 1 ; i <= n ; i ++) jc[i] = jc[i - 1] * i % MOD;dp[1][0][0][0] = dp[1][1][0][1] = 1;for (int i = 2 ; i <= n ; i ++) {for (int j = 0 ; j <= i ; j ++) {// 选 i - 1dp[i][j][0][0] = dp[i - 1][j - 1][0][0] , dp[i][j][1][0] = dp[i - 1][j - 1][0][1];// 选 i + 1dp[i][j][0][1] = (dp[i - 1][j - 1][0][0] + dp[i - 1][j - 1][1][0]) % MOD , dp[i][j][1][1] = (dp[i - 1][j - 1][0][1] + dp[i - 1][j - 1][1][1]) % MOD;// 不产生贡献dp[i][j][1][0] = (dp[i][j][1][0] + dp[i - 1][j][0][1] + dp[i - 1][j][1][1]) % MOD;dp[i][j][0][0] = (dp[i][j][0][0] + dp[i - 1][j][0][0] + dp[i - 1][j][1][0]) % MOD;}}for (int i = m ; i <= n ; i ++) ans = (ans + (dp[n][i][1][0] + dp[n][i][0][0]) % MOD * jc[n - i] % MOD * C(i , m) % MOD * ((i - m & 1) ? -1 : 1) % MOD + MOD) % MOD; // 二项式反演cout << ans;return 0;
}

这篇关于「Positions in Permutations」Solution的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

AtCoder Beginner Contest 370 Solution

A void solve() {int a, b;qr(a, b);if(a + b != 1) cout << "Invalid\n";else Yes(a);} B 模拟 void solve() {qr(n);int x = 1;FOR(i, n) FOR(j, i) qr(a[i][j]);FOR(i, n) x = x >= i ? a[x][i]: a[i][x];pr2(

LeetCode 46 Permutations + LeetCode 47 Permutations II

题意: 给出一串(46题)不重复or(47题)有重复的数字,要求输出所有排列。 思路: 有没有重复不影响思路 = =。 代码展示为46题提交结果,47题一样过…… 可以偷懒用next_permutation方法也可以自己实现,实现方法为从后往前找第一个出现的nums[i] < nums[i+1],从i后面找出比nums[i]稍大一点的数字nums[x],交换nums[i]和nums[

leetcode 刷题之路 12 Permutations

Given a collection of numbers, return all possible permutations. For example, [1,2,3] have the following permutations: [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1]. 全排列问题,详细分析参考这篇文章

error on “nvm list available“, find the final solution by the Second error

error one Could not retrieve https://nodejs.org/dist/index.json. Get “https://nodejs.org/dist/index.json”: dial tcp 104.20.23.46:443: i/o timeout error two Error retrieving “http://npm.taobao.org/m

Leetcode94: Permutations

Given a collection of numbers, return all possible permutations. For example, [1,2,3] have the following permutations: [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1]. 解题思路:字符交换加dfs。 将第

46. Permutations, 47. Permutations II

Given a collection of distinct numbers, return all possible permutations. For example, [1,2,3] have the following permutations: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 无重复数

UVa 11077 Find the Permutations / 置换

把一个排列p变成1,2,...,n可以反过来看成1,2,...,n到p 把p分解乘循环 如果一个循环有n个元素 需要n-1次交换 dp[i][j] = dp[i-1][j] + dp[i-1][j-1] * (i-1); 代表i个元素 交换j次变成1,2,...,n,的种数 对于元素i 他可以自己成为一个循环那么交换次数不变 种数+1 就是 dp[i-1][j]

Deep in MTK Turnkey Solution Logging Tools

一个完整的日志系统除了Log保存机制以外,还要有Log查看机制。不管是Kernel Log还是Android Log都会将Log打印到buffer,那么Log工具则会将Buffer里面的Log拿出来做相应的处理,或者打印到终端,或者对Log做解析以及过滤等等。而Kernel Log除了打印到buffer以外还会打印到Console,那么从console获取Log也是一种常见的方式。 那到底都

Python typeError: a bytes-like object is required, not ‘str’ Solution

目录 一、需求 二、报错 三、解决方法 一、需求 调接口解析其中 dis 字段。 二、报错 Python Typeerror a bytes-like object is required not ‘str’ 这句话的意思是“类型错误:需要类似字节的对象,而不是字符串”。 三、解决方法 在需要解析的字段前 加上 b 原代码: if 'dis' in response

codeforces 463D Gargari and Permutations

题意: 从所给的序列中找出它们的最长公共子序列。 思路: DAG(有向无环图)+BFS。 如果数值 i 在所有序列中都在 j 前面。则i -> j连一条有向边。(好像还有个dp的思路的) 参考:http://www.cnblogs.com/hujunzheng/p/3947392.html AC代码: #include <cstdio>#include <cstring>#i