AGC005D - ~K Perm Counting
Solution
经典数排列个数题,写了个大麻烦容斥。
直接容斥,考虑求出fif_ifi表示有iii个位置∣pi−i∣=k|p_i-i|=k∣pi−i∣=k的方案数。一个位置iii满足∣pi−i∣=k|p_i-i|=k∣pi−i∣=k,要么pi=i+kp_i=i+kpi=i+k,要么pi=i−kp_i=i-kpi=i−k,当前面的位置选择了pi=i+kp_i=i+kpi=i+k时,后面的pi+2kp_{i+2k}pi+2k就不能选择i+ki+ki+k了,前面可能对后面产生影响。
因此我们发现对于下标modkmod\;kmodk不同的位置之间一定没有影响,于是我们对modkmod\;kmodk余数分类,拉出nmodkn\mod knmodk条长为⌊nk⌋+1\lfloor\frac{n}{k}\rfloor+1⌊kn⌋+1的链和k−nmodkk-n\mod kk−nmodk条长为⌊nk⌋\lfloor\frac{n}{k}\rfloor⌊kn⌋的链,此时∣pi−i∣=k|p_i-i|=k∣pi−i∣=k等价于选择了该点在链上的相邻点,把这些链的fff值用背包合并起来就是我们要求的fff。
于是我们要对每一种jjj,算出一条链上选取jjj个的方案数。令gi,j,kg_{i,j,k}gi,j,k表示前iii点,选取jjj个相邻点,i−1,i,i+1{i-1,i,i+1}i−1,i,i+1的选取状态为kkk的方案数。
最后的答案要算上没选的点的任意选取的排列个数,即:
Ans=∑i=0n(−1)ifi(n−i)!Ans=\sum_{i=0}^n(-1)^if_i(n-i)!Ans=i=0∑n(−1)ifi(n−i)!
时间复杂度O(n2)O(n^2)O(n2),据说也有O(nlgn)O(nlgn)O(nlgn)的更麻烦做法。
Code
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se secondusing namespace std;template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=924844033;
const int MAXN=2005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{int f=1,x=0; char c=getchar();while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }return x*f;
}
int num=0,h[MAXN],g[MAXN][MAXN][8],f[MAXN],fac[MAXN],tmp[MAXN];
int upd(int x,int y) { return x+y>=mods?x+y-mods:x+y; }
void Merge(int q)
{for (int j=0;j<=q;j++) {h[j]=0;for (int t=0;t<4;t++) h[j]=upd(h[j],g[q][j][t]);}num+=q;for (int j=0;j<=num;j++) tmp[j]=0;for (int j=num;j>=0;j--)for (int k=0;k<=q;k++) if (j>=k) tmp[j]=upd(tmp[j],1ll*f[j-k]*h[k]%mods);for (int j=0;j<=num;j++) f[j]=tmp[j];
}
signed main()
{int n=read(),p=read(),q=n/p+1;fac[0]=1;for (int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mods;g[1][0][0]=g[1][1][4]=1;for (int i=1;i<q;i++)for (int j=0;j<=i;j++){for (int k=0;k<8;k++) g[i+1][j][k>>1]=upd(g[i+1][j][k>>1],g[i][j][k]);for (int k=0;k<8;k++){if (!((k>>1)&1)) g[i+1][j+1][(k>>1)|1]=upd(g[i+1][j+1][(k>>1)|1],g[i][j][k]);if (!((k>>1)&4)) g[i+1][j+1][(k>>1)|4]=upd(g[i+1][j+1][(k>>1)|4],g[i][j][k]);}}f[0]=1;int r=n%p; for (int i=1;i<=r;i++) Merge(q);for (int i=1;i<=p-r;i++) Merge(q-1);int ans=0;for (int i=0;i<=n;i++) if (i&1) ans=upd(ans,mods-1ll*f[i]*fac[n-i]%mods);else ans=upd(ans,1ll*f[i]*fac[n-i]%mods);printf("%d\n",ans);return 0;
}