Codeforces Round #663 (Div. 2) C. Cyclic Permutations ###K ###K //K

题目链接:https://codeforces.ml/contest/1391/problem/C

题意:每个数会和左边最近的大于他的数的下标连接一条无向边,和左右最近的大于他的数的下标也连接一条,问该排列有环的情况有多少种

思路:模拟一下就很容易发现,只要有2 1 3  某个数左右两边都有一个数大于他,就至少存在一个三元环

那么难点就在于如何求出这些情况, 要么正面求要么反面求,正面求没有求出来,有重复的很难处理,反面求的话就是n!减去无环的情况

其实观察4的样例 猜测一下 就能猜得到答案n!-2^(n-1)  这种组合数多数和2的几次方有关系的 

如果求的话,  就是一个数一个数的放, 1的话只有一种情况, 2的话可以放1的左右两边2种情况,3的话只能放2的左右两边如果放其他位置都会产生上述情况产生环

每个数只能放上一个数的左右两边,共n-1个数可选,所以是2^(n-1)。

 

另外一种考虑方法, 就是能看出来只有先递增后递减 或者单增单减这种情况才满足条件     这样就是熟悉的放数方法了, 在排列里面每个数都是只有2个位置来放

那么我们考虑以最大的n为中心,枚举左边有多少个数即可, 枚举了左边有多少个数,剩下的数都在右边,因为右边都是递减,所以右边的就确定下来了

所以是累加C(i,n-1)  i从0到n-1  累加后也就是排列组合的二项式系数的公式 =2^(n-1)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define pb push_back
 5 const int maxn =1e5+10;
 6 const int mod=1e9+7;
 7 
 8 
 9 ll power(ll base,ll n)
10 {
11     ll r=1;
12     while(n)
13     {
14         if(n%2) r=r*base%mod;
15         base=base*base%mod;
16         n/=2;
17     }
18     return r;
19 }
20 
21 
22 int main()
23 {
24     ios::sync_with_stdio(false);
25     cin.tie(0);
26     ll n;
27     cin>>n;
28     ll temp=1;
29     for(int i=1;i<=n;i++)
30         temp=temp*i%mod;
31     ll ans=temp-power(2,n-1);
32     ans=(ans+mod)%mod;
33     cout<<ans<<'\n';
34 
35 
36 
37 
38 
39 }
View Code

 

posted @ 2020-08-10 15:08  canwinfor  阅读(227)  评论(0)    收藏  举报