我是靠谱客的博主 老实跳跳糖,这篇文章主要介绍题解-Atcoder_agc005D ~K Perm Counting,现在分享给大家,希望可以做个参考。

Problem

AtCoder-agc005D

题意概要:给出(n,k),求合法的排列个数,其中合法定义为任何数字所在位置与自身值差的绝对值不为(k)(即求排列({A_i}),使得(forall iin[1,n],|a_i-i|not =k)

Solution

刚看这道题时除了全集取反搞容斥外没有任何思路啊

(f_i)表示排列中至少有(i)对冲突的方案数,一对冲突定义为存在一个(i)使得(|a_i-i|=k)

考虑全集取反,加上一点点容斥思想可得

[Ans=sum_{i=0}^n(-1)^icdot P_n^icdot f_i]

至于怎么得到 (f_i),就是这道题难点所在,关键思路是画图

构建一个二分图:

  • 其中 (i) 为数值 (i)(i')(i) 在排列中的位置编号
  • 构建边为冲突,即所有 (i') 要和 (ipm k) 连边

就像这样(模拟 (n=4,k=1) 的情况):

纯手绘,图丑勿喷

发现这个二分图中其实只有(2k)条链,于是可以对这(2k)条链进行Dp

在某条链上:设(g[i][j][0/1])表示考虑前(i)个点,且已经有(j)对冲突,(i)号与(i+1)号连与不连的方案数,得出转移方程:

[g[i][j][0]=g[i-1][j][0]+g[i-1][j][1]\g[i][j][1]=g[i-1][j-1][0]]

对于每条链的(f[i])即为(g[end][i][0]+g[end][i][1])(end)为链的末尾),最后合并(2k)条链的时候可以玩背包

但实际上有个小技巧,就是将(2k)条链首尾顺次相接,在两条链的交界处不转移第二个方程即可

Code

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <cstdio> const int N=2040,p=924844033; int f[N+N][N][2]; bool end[N+N]; int n,k,Ans,fac[N]; inline int qm(int x){return x<p?x:x-p;} int main(){ scanf("%d%d",&n,&k);fac[0]=1; for(int i=1;i<=n;++i)fac[i]=1ll*fac[i-1]*i%p; for(int i=1,tt=0,d;i<=k;++i){ d=(n-i)/k+1; tt+=d,end[tt]=true; tt+=d,end[tt]=true; } f[1][0][0]=1; for(int i=1;i<=n+n;++i) for(int j=0;j<=n;++j){ f[i+1][j][0]=qm(f[i][j][0]+f[i][j][1]); if(!end[i])f[i+1][j+1][1]=f[i][j][0]; } for(int i=0,t;i<=n;++i){ t=1ll*fac[n-i]*qm(f[n+n][i][0]+f[n+n][i][1])%p; if(i&1)Ans=qm(Ans-t+p); else Ans=qm(Ans+t); } printf("%dn",Ans); return 0; }

转载于:https://www.cnblogs.com/penth/p/10158684.html

最后

以上就是老实跳跳糖最近收集整理的关于题解-Atcoder_agc005D ~K Perm Counting的全部内容,更多相关题解-Atcoder_agc005D内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(138)

评论列表共有 0 条评论

立即
投稿
返回
顶部