「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 一定未被选取,且 i − 1 i-1 i1 在之前一定未被选取。
    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 - 1
            dp[i][j][0][0] = dp[i - 1][j - 1][0][0] , dp[i][j][1][0] = dp[i - 1][j - 1][0][1];
            // 选 i + 1
            dp[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;
}
  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
如果您是指在自然语言处理中,如何使用实体(entity)来获取位置(positions),那么您可以使用命名实体识别(NER)来识别文本中的实体,并使用文本处理技术来提取实体的位置信息。 在命名实体识别中,您可以使用现有的NLP工具库(如spaCy, NLTK等)来标注文本中的实体类型,例如人名、地名、组织机构等。然后,您可以使用文本处理技术(如正则表达式、NLP规则等)来提取实体的位置信息。例如,您可以通过查找实体在文本中的开始和结束位置来提取实体的位置信息。 以下是一个简单的Python代码示例,演示如何使用spaCy和正则表达式来提取文本中的实体位置信息: ```python import spacy import re nlp = spacy.load('en_core_web_sm') text = "John Smith is from New York City and works at Microsoft." doc = nlp(text) for ent in doc.ents: if ent.label_ == 'GPE': # 只提取地名实体 pattern = re.compile(ent.text) match = pattern.search(text) if match: start = match.start() end = match.end() print(ent.text, start, end) ``` 在上述代码中,我们首先使用spacy加载英文模型,然后使用该模型对文本进行命名实体识别。在这个例子中,我们只提取地名实体(GPE)。然后,我们使用正则表达式在文本中查找实体,并获取实体在文本中的开始和结束位置。 需要注意的是,这只是一个简单的示例,实际应用中可能需要更复杂的文本处理技术来提取实体的位置信息。同时,NER的准确性也取决于您所使用的NLP工具库和模型的质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值