Codeforces 285 E. Positions in Permutations

\(>Codeforces \space 285 E. Positions in Permutations<\)

题目大意 : 定义一个长度为 \(n\) 的排列中第 \(i\) 个元素是好的,当且仅当 \(i\)在排列中的位置 \(p_i\) 满足 \(|i - p_i| = 1\), 给出 \(n, k\) 求长度为 \(n\) 的排列中恰好有 \(k\) 个元素是好的方案数

$1 \leq n \leq 1000, 0 \leq k \leq n $

解题思路 :

观察发现,直接求出答案貌似十分困难,不妨先 \(dp\) 出至少 \(k\) 个元素是好的方案数,然后再通过 容斥/反演 来解决

至少有 \(k\) 个元素是好的方案数等价于有 \(k\) 个位置存在匹配 \(|i-p_i| = 1\) ,剩余元素随便排列的方案数

设 \(f[i][j][0/1][0/1]\) 表示前 \(i\) 个元素和前 \(i\) 个位置产生了 \(j\) 对匹配,第 \(i\) 个元素和第 \(i\) 个位置是否已经被匹配的方案数

转移很显然,只需要枚举 \(i-1\) 上的元素和位置是否能和 \(i\) 上的元素和位置产生匹配即可

设 \(F(k)\) 表示至少有 \(k\) 个是好的方案数,那么 \(k = f[n][k] \times(n-k)!\)

考虑要求 \(Ans_k =\) 恰好有 \(k\) 个元素是好的方案数,那么有 \(Ans_k = \sum_{i = k}^{n} (-1)^{i-k} \times C_i^k \times F(i)\)

这个和最基础的容斥又有所不一样,简单的考虑,对于 \(i > k\) 的每一个 \(Ans_i\) ,其中任意选 \(k\) 个好元素都能更新到 \(Ans_k\) ,所以方案数是 \(C_i^k\) ,在容斥进行补集转换的时候需要乘上这个系数

往复杂了讲,这个涉及到容斥原理最本质的在集合上的运算,之前的 \(i\) 将表示所有大小为 \(i\) 的集合,那么每一个大小为 \(i\) 的集合在算到 \(Ans_k\) 的过程中都产生了 \(C_i^k\) 个不同的大小为 \(k\) 的集合,当中的具体证明可以看近年集训队论文

考虑到前面算 \(F(i)\) 的时候,两个不同的匹配方案可能会对应到同一个排列,这里等价于每一个大小为 \(i\) 的集合,选 \(k\) 个好元素得到的新集合可能相同,所以两个重复计数恰好抵消了

/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int f = 0, ch = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == ‘-‘) f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
#define N (2005)
#define int ll
const int Mod = 1000000007;
int js[N], inv[N], f[N][N][2][2], g[N], n, k;
inline int pow(int a, int b){
    int ans = 1;
    for(; b; b >>= 1, a = a * a % Mod)
        if(b & 1) ans = ans * a % Mod;
    return ans;
}
inline int C(int n, int i){ return js[n] * inv[i] % Mod * inv[n-i] % Mod; }
main(){
    read(n), read(k), f[0][0][0][0] = 1, js[0] = inv[0] = 1;
    for(int i = 1; i <= n; i++){
        js[i] = js[i-1] * i % Mod;
        inv[i] = pow(js[i], Mod - 2);
    }
    for(int i = 1; i <= n; i++)
        for(int j = 0; j <= n; j++){
            (f[i][j][0][0] += f[i-1][j][0][0] + f[i-1][j][1][1]) %= Mod;
            (f[i][j][0][0] += f[i-1][j][0][1] + f[i-1][j][1][0]) %= Mod;
            if(j >= 1 && i > 1){
                (f[i][j][1][0] += f[i-1][j-1][1][0] + f[i-1][j-1][0][0]) %= Mod;
                (f[i][j][0][1] += f[i-1][j-1][0][1] + f[i-1][j-1][0][0]) %= Mod;
            }
            if(j >= 2 && i > 1) (f[i][j][1][1] += f[i-1][j-2][0][0]) %= Mod;
        }
    for(int i = 0; i <= n; i++){
        int res = 0;
        (res += f[n][i][0][0] + f[n][i][1][0]) %= Mod;
        (res += f[n][i][0][1] + f[n][i][1][1]) %= Mod;
        g[i] = res * js[n-i] % Mod;
    }
    int ans = 0;
    for(int i = k; i <= n; i++){
        int p = ((i - k) & 1) ? Mod - 1 : 1;
        (ans += C(i, k) * p % Mod * g[i] % Mod) %= Mod;
    }
    cout << ans << endl;
    return 0;
}

原文地址:https://www.cnblogs.com/mangoyang/p/9368346.html

时间: 2024-11-14 12:34:53

Codeforces 285 E. Positions in Permutations的相关文章

CF285 E Positions in Permutations——“恰好-&gt;大于”的容斥和允许“随意放”的dp

题目:http://codeforces.com/contest/285/problem/E 是2018.7.31的一场考试的题,当时没做出来. 题解:http://www.cnblogs.com/yanshannan/p/9410986.html 因为那个值对于 i 位置来说只和 i 位置放了 i-1 或 i+1 有关,所以状态里记录一下 i 和 i+1 有没有已经放过,再加上 i-1 的对于 i-1 和 i 的状态,就能转移了. 枚举这一位:放 i-1 /放 i+1/先空下.先空下对那个值无

codeforces #285 div.2 C. Misha and Forest

题目链接:    http://codeforces.com/contest/501/problem/C 题意:    给出图的每个顶点的度数和相邻顶点的异或值的和,求出这个图的边的情况和边的数目: 分析:   找出度为一的点,其所对应的异或值即为与之相邻的顶点(<- ^ ->),再将与之相邻顶点的度数减一: **********代码: #include<iostream> #include<cstdio> #include<cstring> #includ

【codeforces #285(div 1)】

打完这场,从紫名回到蓝名了 A. Misha and Forest time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Let's define a forest as a non-directed acyclic graph (also without loops and parallel edges). One day Mish

CF285E Positions in Permutations(dp+容斥)

题意,给定n,k,求有多少排列是的 | p[i]-i |=1 的数量为k. Solution 直接dp会有很大的后效性. 所以我们考虑固定k个数字使得它们是合法的,所以我们设dp[i][j][0/1][0/1]表示前i个数,填了j个数,当前位置有没有被选,下一位有没有被选,这样做的话,转移会比较简单. 那么除去这j个数,剩下的数随便填,乘上全排列就好了. 但这样会多算. 然后这种问题有一个容斥模型,直接套上就好了. #include<iostream> #include<cstdio&g

【CF285E】Positions in Permutations

题目 刷水题涨信心 显然这是个广义容斥,我们现在算一下至少有\(i\)个完美数的方案数就好了 这\(1000\)的数据范围显然在暗示\(n^2\)的dp 我们注意到这个条件大概就是\(P_i=i-1\)或\(P_i=i+1\),于是我们可以想象成左右两边各\(n\)个点去完成一个一一匹配 设\(dp[i][j][k][p]\)表示左边第\(i\)个数已经匹配完了,一共形成了\(j\)对完美数,\(k\)表示右边对应的第\(i\)个位置的使用状态\(0/1\),\(p\)表示右边第\(i+1\)个

最近两场比赛 CF 285 &amp; TC 646

Codeforces 285 这场rating又掉了,好在只掉了十多. 题目比较水,但是我比赛时居然只艰辛地过了前两道. 504A 由于图是森林,所以一定有度为1的点,把这些点删了后图还是森林.然后就完了.比赛的时候居然把森林当成了树,交了3次才过. 504B 把排列表示为一个n元组\( (p_0, p_1, \cdots, p_{n - 1})\),其中\(0 \leqslant p_i \leqslant i \),其排名为 \[\sum_{i = 0}^{n - 1}{p_ii!}\].

YCB 的暑期计划

前言 YCB现在很弱(TAT) 暑假有一个月,赶快狂补一下. 大概的计划如下: 首先前期会以数据结构为主,毕竟代码能力太弱,涉及内容:线段树分治.二进制分组.KD-Tree. 等数据结构做到没有智商的时候加入一波数论,内容为 杜教筛.min_25筛. 然后中途小清新一下,做一些 组合博弈与构造题. 接着继续练代码能力,顺便学一些神奇的暴力:启发式合并.dsu on tree . 然后图论也忘的差不多了,就回过头去学点新东西,大概会有spfa判负环.0/1分数规划.差分约束. 估计这个时候也没有什

7月好题记录

Codeforces 1063 B. Labyrinth [确定性]给出一个迷宫\((1 \leq n,m \leq 2000)\),求从起点到各个点,能够做到在左移动次数不超过\(x\)次,右移动次数不超过\(y\)次的情况下到达的点的个数. 显然贪心地要求到达每个点时左右移动次数越少越好,但是两个关键字很难维护.而事实上,起点终点确定时,左移动次数确定时右移动次数一定确定.当终点在起点左侧时,最小化左移动次数即可:当终点在起点右侧时,依然最小化左移动次数即可.当然两者都最小化右移动次数也可以

Codeforces Round #285 (Div.1 B &amp; Div.2 D) Misha and Permutations Summation --二分+树状数组

题意:给出两个排列,求出每个排列在全排列的排行,相加,模上n!(全排列个数)得出一个数k,求出排行为k的排列. 解法:首先要得出定位方法,即知道某个排列是第几个排列.比如 (0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0). 拿排列(1,2,0)来说,首位是1,前面有cnt=1个小于1的没被用过的数(0),所以它的排行要加上(cnt=1)*2!,第二位为2,因为1已经放了,所以小于2的只有0了,即cnt=1个,所以,排