CF 285 E Positions in Permutations 题解

89 篇文章 1 订阅

题目链接

首先这题是要求求出恰好有k个的方案数。

我们假设是 f ( k ) f(k) f(k)

z ( s ) z(s) z(s)表示钦定集合 s s s里的位置一定是特殊的的方案数。

我们设 g ( k ) = ∑ ∣ s ∣ = k z ( s ) g(k)=\sum_{{|s|=k}}z(s) g(k)=s=kz(s)

可以发现 g ( k ) = ∑ k ′ ≥ k f ( k ′ ) ∗ ( k ′ k ) g(k)=\sum_{k\prime \geq k} f(k\prime)*\binom{k\prime}{k} g(k)=kkf(k)(kk)

根据二项式反演,可以推出。

f ( k ) = ∑ k ′ ≥ k ( − 1 ) k ′ − k ∗ g ( k ′ ) ∗ ( k ′ k ) f(k)=\sum_{k\prime\geq k} (-1)^{k\prime-k}*g(k\prime)*\binom{k\prime}{k} f(k)=kk(1)kkg(k)(kk)

其实这可以用容斥的思想来证明。

这样问题就转换成了怎么来求出 z ( s ) z(s) z(s)

我们可以用 d p dp dp来求。
d p i , j , m a s k 表 示 考 虑 前 i 个 , 已 经 使 用 了 j 个 , i − 1 , i , i + 1 的 是 否 使 用 的 状 态 为 m a s k d p i , j , m a s k → d p i + 1 , j ′ , n e w _ m a s k dp_{i,j,mask}表示考虑前i个,已经使用了j个,i-1,i,i+1的是否使用的状态为mask\\ dp_{i,j,mask}\rightarrow dp_{i+1,j\prime,new\_mask} dpi,j,maski使ji1,i,i+1使maskdpi,j,maskdpi+1,j,new_mask
这个转移也比较容易,最后 ∑ ∣ s ∣ = k z ( s ) = ∑ d p n , k , ∗ \sum_{|s|=k} z(s)=\sum dp_{n,k,*} s=kz(s)=dpn,k,

Code:

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int MOD=1e9+7;
int f[1001][1001][1<<3];
int n,k;
void add(int& x,int y){
	x+=y;
	if(x>=MOD)
		x-=MOD;
}
LL fact[1001],rfact[1001];
LL quick(int A,int B){
	if(!B) return 1ll;
	LL rest=quick(A,B>>1);
	rest*=rest;
	rest%=MOD;
	if(B&1) rest=rest*A%MOD;
	return rest;
}
int c(int A,int B){
	return fact[A]*rfact[B]%MOD*rfact[A-B]%MOD;
}
int main(){
	R2(n,k);
	f[0][0][0]=1;
	fact[0]=1;
	rb(i,1,1000)
		fact[i]=fact[i-1]*i%MOD;
	rfact[1000]=quick(fact[1000],MOD-2);
	rl(i,999,1){
		rfact[i]=rfact[i+1]*(i+1)%MOD;
	}
	rfact[0]=1;
	rep(i,n){
		rep(j,i+1){
			rep(mask,1<<3){
				if(f[i][j][mask]){
					int nex_mask=mask;
					nex_mask<<=1;
					nex_mask&=(1<<3)-1;
					if(i+2<=n){ 
						add(f[i+1][j+1][nex_mask+1],f[i][j][mask]);
					}
					if(i>=1&&!(nex_mask&4)){ 
						add(f[i+1][j+1][nex_mask+4],f[i][j][mask]);
					}
					add(f[i+1][j][nex_mask],f[i][j][mask]);  
				}
			}
		}
	} 
	int rest=0;
	rb(kk,k,n){
		int res=0;
		rep(mask,1<<3){
			add(res,f[n][kk][mask]);
		}
		rb(i,1,n-kk){
			res=1ll*res*i%MOD;
		}
		if((kk-k)&1){
			res=1ll*res*c(kk,k)%MOD;
			res=MOD-res;
			rest+=res;
			rest%=MOD;
		}
		else{
			add(rest,1ll*res*c(kk,k)%MOD);
		}
	}
	cout<<rest<<endl;
	return 0;
}
/*
20 2
483564804

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值