codeforces 340E Iahub and Permutations(错排or容斥)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud

Iahub and Permutations

Iahub is so happy about inventing bubble sort graphs that he‘s staying all day long at the office and writing permutations. Iahubina is angry that she is no more important for Iahub. When Iahub goes away, Iahubina comes to his office and sabotage his research work.

The girl finds an important permutation for the research. The permutation contains n distinct integers a1, a2, ..., an (1 ≤ ai ≤ n). She replaces some of permutation elements with -1 value as a revenge.

When Iahub finds out his important permutation is broken, he tries to recover it. The only thing he remembers about the permutation is it didn‘t have any fixed point. A fixed point for a permutation is an element ak which has value equal to k (ak = k). Your job is to proof to Iahub that trying to recover it is not a good idea. Output the number of permutations which could be originally Iahub‘s important permutation, modulo 1000000007 (109 + 7).

Input

The first line contains integer n (2 ≤ n ≤ 2000). On the second line, there are n integers, representing Iahub‘s important permutation after Iahubina replaces some values with -1.

It‘s guaranteed that there are no fixed points in the given permutation. Also, the given sequence contains at least two numbers -1 and each positive number occurs in the sequence at most once. It‘s guaranteed that there is at least one suitable permutation.

Output

Output a single integer, the number of ways Iahub could recover his permutation, modulo 1000000007 (109 + 7).

Sample test(s)

input

5-1 -1 4 3 -1

output

2

Note

For the first test example there are two permutations with no fixed points are [2, 5, 4, 3, 1] and [5, 1, 4, 3, 2]. Any other permutation would have at least one fixed point.

有一个序列,要求你将其中的-1换成1到n中的一个数,使得其成为1到n的一个排列,但要求第i为不得放i,问有几种替换的方法。

分析:

一个典型的错排问题,考虑到有部分数其本身的位置已被摆放,但其本身还没有用到,即这部分数是无限制的排列。

有一部分数是其本身的位置还是-1,但其本身已被使用,这些数的数目与上一种数必定是相同的,这类数已被放置,也无需考虑。

有一部分数是其本身的位置已被摆放,其本身也已被使用,故这类数不会对排列产生影响,也不必考虑。

剩下一部分数是其本身的位置还是-1,其本身也还未被用到,即这一部分的数是有限制的排列。

从而,我们只需要第一类数和第四类数。

假设摆放j个有限制排列的数时的方法为dp[j],设总共有tx个无限制的数。

1.把一个有限制的数摆放到无限制的数所对应的tx个位置上时。。。仔细想想,这就相当于把一个无限制的数摆放到有限制的数的位置,则原来的那个有限制的数变成了无限制的数,即有限制的数减少了1,而无限制的数的数目不变,则有tx*dp[j-1]种。

2.把一个有限制的数摆放到j-1个有限制的数位置上时,且a[i]=j,a[j]=i;则共有(j-1)*dp[j-2]。

3.把一个有限制的数摆放到j-1个个有限制的数位置上时,且a[i]=j,a[j]!=i;则共有(j-1)*dp[j-1]。

所以dp[j]=tx*dp[j-1]+(j-1)*dp[j-2]+(j-1)*dp[j-1];

然后,在把剩下的tx个摆好,就是tx的阶乘。

然后,就没然后了,就AC了。。。

======================

当然这道题也可以用容斥做,考虑a[i]==i的个数,总数去掉这些就是答案了。

 1 //#####################
 2 //Author:fraud
 3 //Blog: http://www.cnblogs.com/fraud/
 4 //#####################
 5 #include <iostream>
 6 #include <sstream>
 7 #include <ios>
 8 #include <iomanip>
 9 #include <functional>
10 #include <algorithm>
11 #include <vector>
12 #include <string>
13 #include <list>
14 #include <queue>
15 #include <deque>
16 #include <stack>
17 #include <set>
18 #include <map>
19 #include <cstdio>
20 #include <cstdlib>
21 #include <cmath>
22 #include <cstring>
23 #include <climits>
24 #include <cctype>
25 using namespace std;
26 #define XINF INT_MAX
27 #define INF 0x3FFFFFFF
28 #define MP(X,Y) make_pair(X,Y)
29 #define PB(X) push_back(X)
30 #define REP(X,N) for(int X=0;X<N;X++)
31 #define REP2(X,L,R) for(int X=L;X<=R;X++)
32 #define DEP(X,R,L) for(int X=R;X>=L;X--)
33 #define CLR(A,X) memset(A,X,sizeof(A))
34 #define IT iterator
35 typedef long long ll;
36 typedef pair<int,int> PII;
37 typedef vector<PII> VII;
38 typedef vector<int> VI;
39 const ll MOD =1000000007 ;
40 int a[10010];
41 ll b[2010];
42 bool vis[2010];
43 ll dp[2010];
44 int main()
45 {
46     ios::sync_with_stdio(false);
47     int n;
48     cin>>n;
49     b[0]=1;
50     for(int i=1;i<=n;i++)cin>>a[i];
51     for(ll i=1;i<2010;i++)b[i]=(b[i-1]*i)%MOD;
52     int tx=0,ty=0;
53     for(int i=1;i<=n;i++){
54         if(a[i]!=-1)vis[a[i]]=1;
55     }
56     for(int i=1;i<=n;i++){
57         if(a[i]==-1){
58             if(vis[i])tx++;
59             else ty++;
60         }
61     }
62     dp[0]=1;
63     dp[1]=tx*dp[0]%MOD;
64     for(int i=2;i<=ty;i++){
65         dp[i]=(tx*dp[i-1]%MOD+(i-1)*dp[i-1]%MOD+(i-1)*dp[i-2]%MOD)%MOD;
66     }
67     cout<<dp[ty]*b[tx]%MOD<<endl;
68
69
70
71
72
73     return 0;
74 }

代码君

时间: 2024-10-22 02:52:50

codeforces 340E Iahub and Permutations(错排or容斥)的相关文章

CodeForces 340E Iahub and Permutations 错排dp

Iahub and Permutations 题解: 令 cnt1 为可以没有限制位的填充数字个数. 令 cnt2 为有限制位的填充数字个数. 那么:对于cnt1来说, 他的值是cnt1! 然后我们对cnt2进行dp. 对于任意一个新加进来的数字,我们可以令一个一个没有限制位数放在这里, 那么新加进来的数字 ≍ 没有限制位, 他的方案为 i-1 * dp[i-1] , 然后我们如果把这个数字放到有限制位的数来说, 那么他的转移方程就和错排一样了. 代码: #include<bits/stdc++

不容易系列之一(错排公式+容斥)

不容易系列之一 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 17475    Accepted Submission(s): 7284 Problem Description 大家常常感慨,要做好一件事情真的不容易,确实,失败比成功容易多了! 做好“一件”事情尚且不易,若想永远成功而总从不失败,那更是难上加难了,就像花钱总是比挣钱容

CodeForces 340E Iahub and Permutations

容斥原理,组合数. 找出有$cnt$个数字还有没放,那么总方案数就是$cnt!$. 总方案数里面包含了正确的和非正确的,我们需要将非正确的删去. 先删去$1$个数字$a[i]=i$的情况,发现会多删,要加回两个数字$a[i]=i$的情况,发现会多加......就是一个容斥原理的过程. #pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring>

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 838A - Binary Blocks(二维前缀和+容斥)

838A - Binary Blocks 思路:求一下前缀和,然后就能很快算出每一小正方块中1的个数了,0的个数等于k*k减去1的个数,两个的最小值就是要加进答案的值. 代码: #include<bits/stdc++.h> using namespace std; #define ll long long #define pb push_back #define mem(a,b) memset((a),(b),sizeof(a)) const int INF=0x3f3f3f3f; cons

codeforces 439 E. Devu and Birthday Celebration 组合数学 容斥定理

题意: q个询问,每一个询问给出2个数sum,n 1 <= q <= 10^5, 1 <= n <= sum <= 10^5 对于每一个询问,求满足下列条件的数组的方案数 1.数组有n个元素,ai >= 1 2.sigma(ai) = sum 3.gcd(ai) = 1 solution: 这道题的做法类似bzoj2005能量采集 f(d) 表示gcd(ai) = d 的方案数 h(d) 表示d|gcd(ai)的方案数 令ai = bi * d 则有sigma(bi)

BZOJ 4517: [Sdoi2016]排列计数 错排+逆元

4517: [Sdoi2016]排列计数 Description 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳定的.序列恰好有 m 个数是稳定的 满足条件的序列可能很多,序列数对 10^9+7 取模. Input 第一行一个数 T,表示有 T 组数据. 接下来 T 行,每行两个整数 n.m. T=500000,n≤1000000,m≤1000000 Output 输出 T 行,每行一个数,表示

HDU 2048 错排

错排递推公式: d(n) = (n-1)*(d[n-1]+d[n-2]): 证明:将第n个元素放到第k处,第k处的元素如果放到第n处,就是d(n-2),否则,先假设放到第n处,然后错排,就是d(n-1): 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 double fac[22] = {1,1}; 6 double d[22] = {0,0,1,2}; 7 8 int main() 9 { 10 for(int i=1;i<

错排问题

问题: 十本不同的书放在书架上.现重新摆放,使每本书都不在原来放的位置.有几种摆法? 这个问题推广一下,就是错排问题,是组合数学中的问题之一. 考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排. n个元素的错排数记为D(n). 研究一个排列错排个数的问题,叫做错排问题或称为更列问题. 错排问题最早被尼古拉·伯努利和欧拉研究,因此历史上也称为伯努利-欧拉的装错信封的问题.这个问题有许多具体的版本,如在写信时将n封信装到n个不同的信封里,有