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

题目链接:C. Cyclic Permutations

题意

给你一个数n,让你寻找1~n的全排列中满足以下条件的排列有多少种。
一个排列中,我们可以将一个数左边第一个大于它的数的序号和该数序号连接,也可以将右边第一个大于它的数的序号和该数连接,如果没有这种数就不连接。对每个数操作完后,形成一个无向图,如果无向图有环则满足条件,无环则不满足。

题解

首先通过分析我们可以知道当1在中间时必满足条件。
以a、1、b为例,设1的序号为x,则a的序号为x-1,b的序号为x+1。
可知(x,x+1)和(x,x-1)这两个必定可以连接。
由于a≠b,a和b中必定有一个大所以(x-1,x+1)也可以连接。
x->x+1->x-1->x可知必定有环。
那么1在中间的情况数为:(n-2)*(n-1)!
剩下的情况是分析1在两边时的情况:
计算1在两边时的方法有两种,第一种是找公式,第二种是dp。

第一种,我们很容易发现当序列为单峰序列时是不符合条件的,那么我们可以通过(总排列数-不符合条件数=符合条件数),当1在一边时,剩下共有(n-1)!种排列情况,那么这2~n个排列中单峰序列有多少种?
易知n一定为单峰序列的峰,剩下左右两边都可以摆放数字,那么2~n这(n-1)个数字中有 2 n − 2 {2^{n-2}} 2n2种单峰序列,所以 符 合 条 件 数 = 2 ∗ ( ( n − 1 ) ! − 2 n − 2 ) {符合条件数=2*((n-1)!-2^{n-2})} =2((n1)!2n2)

第二种dp,当1在两边时,1不会和任何数相连,并且本题相连看的是相对大小,所以剩下2~n的完全可以看为1~(n-1),所以1~(n-1)有多少种,1在两边就有多少种。
后一个状态可以由前一个推导而来,完全可以用dp求解。

dp[i]:1~i的全排列中符合条件的个数。
转移方程:dp[i]=f[i]*(n-2)+2*dp[i-1].(f[i]表示:(i-1)! )

代码

公式

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
typedef long double ld;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define pii pair
#define lowbit(x) x&-x

const double PI=acos(-1.0);
const double eps=1e-6;
const ll mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
const int maxm=100+10;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

ll f[maxn];
ll qpow(ll a,ll b)
{
	ll ans=1;
	if(b<0) return 1;
	while(b)
	{
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
int main()
{
	ll n;
	cin >> n;
	ll f1=1;
	for(int i=1;i<n;i++) f1=f1*i%mod;
	ll ans=((f1*(n-2))%mod+2*(f1-qpow(2, n-2)%mod)%mod+mod)%mod;
	cout << ans << endl;
}

dp

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
typedef long double ld;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define pii pair
#define lowbit(x) x&-x

const double PI=acos(-1.0);
const double eps=1e-6;
const ll mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxn=1e6+10;
const int maxm=100+10;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

ll qpow(ll a,ll b)
{
	ll ans=1;
	if(b<0) return 1;
	while(b)
	{
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}

ll dp[maxn],f[maxn];
int main()
{
	ll n;
	cin >> n;
	f[1]=1;
	for(int i=2;i<=n;i++) f[i]=f[i-1]*(i-1)%mod;
	dp[3]=2;
	for(int i=4;i<=n;i++) 
	{
		dp[i]=((f[i]*(i-2)%mod+2*dp[i-1]%mod)+mod)%mod;
	}
	cout << dp[n] << endl;
}

你可能感兴趣的:(dp入门,数学)