「2020-2021 集训队作业」Yet Another Permutation Problem
「2020-2021 集训队作业」Yet Another Permutation Problem
首先是否可以被构造的充要条件: 存在一段上升子串的长度$\geq $ n-k。
考虑容斥:
\(n!-\sum [所有极长上升段的长度<n-k]\)。
可以记录每一个大段(合并后的段)的容斥系数和。
可以发现,只有\(t\cdot k\)的地方是-1,\(t\cdot k+1\)的地方是1,其它是0 。
用调和级数分析,背包的时间复杂度为\(O(N^2\ln N)\) 。
/*
{
######################
# Author #
# Gary #
# 2021 #
######################
*/
#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 check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
//inline int read(){
// int x=0;
// char ch=getchar();
// while(ch<'0'||ch>'9'){
// ch=getchar();
// }
// while(ch>='0'&&ch<='9'){
// x=(x<<1)+(x<<3)+(ch^48);
// ch=getchar();
// }
// return x;
//}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int MAXN=1e3+233;
int n,MOD,h[MAXN],fac[MAXN],rfac[MAXN],dp[MAXN];
int quick(int A,int B){
int ret=1;
while(B){
if(B&1) ret=1ll*A*ret%MOD;
B>>=1;
A=1ll*A*A%MOD;
}
return ret;
}
int inv(int A){return quick(A,MOD-2);}
int main(){
scanf("%d%d",&n,&MOD);
fac[0]=1;
rb(i,1,n) fac[i]=1ll*fac[i-1]*i%MOD;
rb(i,0,n) rfac[i]=inv(fac[i]);
rb(i,0,n-1){
//<n-i
int t=n-i;
int rest=fac[n];
memset(dp,0,sizeof(dp));
dp[0]=1;
vector<mp> obj;
for(int i=0;i<=n;i+=t){
obj.PB({i,MOD-1});
obj.PB({i+1,1});
}
rb(i,1,n){
for(auto it:obj){
if(it.FIR>i) break;
(dp[i]+=1ll*dp[i-it.FIR]*it.SEC%MOD*rfac[it.FIR]%MOD)%=MOD;
}
}
dp[n]=1ll*fac[n]*dp[n]%MOD;
rest-=dp[n];
rest+=MOD;
rest%=MOD;
printf("%d\n",rest);
}
return 0;
}