atcoder 005 D ~K Perm Counting - 容斥 - dp

考虑容斥,发现按照i%2k分类,不同的i互补影响。
首先计数f(i,j)表示同一类中,i个位置有j个不满足限制的方案数,这个显然可以在平方时间复杂度内搞出来。
然后维护出g(i,j)表示i类,n个位置j个不满足限制的方案数。这个注意到i是O(k)级别的,转移是O(n/k)级别的,所以复杂度是平方的。
然后随便选就是阶乘。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define lint long long
#define mod 924844033
#define N 2010
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
int f[2][N][N][2],g[N][N],s[2][N][N],fac[N];
inline int sol(lint x,int s) { return x%=mod,((s&1)?(x?mod-x:0):x); }
inline int getf(int n,int a,int f[N][N][2],int s[N][N])
{
	f[1][1][0]=a,f[1][1][1]=1;
	s[0][0]=1,s[1][0]=1,s[1][1]=1+a;
	for(int i=2;i<=n;i++)
	{
		s[i][0]=1,f[i][0][0]=f[i][0][1]=0;
		for(int j=1;j<=i;j++)
			f[i][j][0]=(s[i-2][j-1]+f[i-1][j-1][0])%mod,
			f[i][j][1]=s[i-1][j-1],
			s[i][j]=(1ll*s[i-1][j]+f[i][j][0]+f[i][j][1])%mod;
	}
	return 0;
}
inline int getabl(int r,int n,int k,int &a,int &b,int &l)
{	return a=(r>k),l=(n-r)/(2*k)+1,b=((2*l-1)*k+r<=n);	}
inline int F(int f[N][N][2],int s[N][N],int l,int b,int t)
{	if(b) return s[l][t];return (s[l-1][t]+f[l][t][0])%mod;	}
int main()
{
	int n,k,mxL,ans=0;scanf("%d%d",&n,&k),mxL=(n-1)/(2*k)+1;
	getf(mxL,0,f[0],s[0]),getf(mxL,1,f[1],s[1]),g[0][0]=1;
	rep(i,0,min(n,2*k)-1)
	{
		int a,b,l;getabl(i+1,n,k,a,b,l);
		rep(j,0,n) if(g[i][j]) rep(t,0,min(n-j,l))
			(g[i+1][j+t]+=1ll*g[i][j]*F(f[a],s[a],l,b,t)%mod)%=mod;
	}
	for(int i=fac[0]=1;i<=n;i++) fac[i]=(lint)fac[i-1]*i%mod;
	for(int i=0;i<=n;i++) (ans+=sol(1ll*fac[n-i]*g[min(n,2*k)][i],i))%=mod;
	return !printf("%d\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值