标签:ch Counting int AGC Perm read 条链 include getchar
D - ~K Perm Counting
题意:
求有多少排列对于每个位置i都满足$|ai−i|!=k$。n<=2000
分析:
容斥+dp。
$answer = \sum\limits_{i = 0}^{n}(-1)^ig[i] \times (n - i)!$
$g[i]$表示至少存在I个位置满足$a[i] - i = k$个数。
考虑如何求出$g[]$。 如果建立两列点,一个表示数字,一个表示下标,左边第i个点与右边第i-k和i+k个点连边,那么这是一张二分图,g[i]就是求满足有刚好i个匹配的方案数。
发现这样图可以按照模k的余数分成2k条链,每条链互不影响,链内满足不能有相邻的一起选。于是可以$f[i][j][0]$表示到链上的第i个位置,当前有j个匹配,第i个选不选的方案数。
考虑如何将2k条链合并:因为链与链之间是互不影响的,所以可以建立一个点,连接两条链,然后让这个点一定不选即可。
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<cctype> #include<set> #include<queue> #include<vector> #include<map> using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; } const int N = 4005, mod = 924844033; int f[N][N][2], g[N], A[N], fac[N]; inline void add(int &x,int y) { x += y; if (x >= mod) x -= y; } int main() { int n = read(), k = read(), Ans = 0; int cnt = 0; for (int i = 1; i <= k; ++i) { for (int j = i; j <= n; j += k) A[++cnt] = (i == j); for (int j = i; j <= n; j += k) A[++cnt] = (i == j); } f[0][0][0] = 1; for (int i = 1; i <= (n << 1); ++i) for (int j = 0; j <= i; ++j) { add(f[i][j][0], (f[i - 1][j][0] + f[i - 1][j][1]) % mod); if (!A[i] && j) add(f[i][j][1], f[i - 1][j - 1][0]); } fac[0] = 1; for (int i = 1; i <= n; ++i) fac[i] = 1ll * fac[i - 1] * i % mod; for (int i = 0; i <= n; ++i) { int res = (f[n << 1][i][0] + f[n << 1][i][1]) % mod; Ans += 1ll * (i & 1 ? -1 : 1) * res * fac[n - i] % mod; Ans = (Ans % mod + mod) % mod; } cout << Ans; return 0; }
标签:ch,Counting,int,AGC,Perm,read,条链,include,getchar 来源: https://www.cnblogs.com/mjtcn/p/10336625.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。