AGC005D - ~K Perm Counting(组合数学,背包,dp)
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; }總結
以上是生活随笔為你收集整理的AGC005D - ~K Perm Counting(组合数学,背包,dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AGC004E - Salvage Ro
- 下一篇: 如何打开.epub和.azw3格式的电子