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 }

浙公网安备 33010602011771号