【PE 559】Permuted Matrices(容斥)

传送门


题解:

用学校的电脑只跑了43s。

发现当我们确定哪些位置下降后,把序列分成若干段,对于每段,我们确定哪些数放进去之后肯定就只有唯一方式了。

考虑枚举 ⌊ n k ⌋ \lfloor\frac{n}{k}\rfloor kn个列里面有有哪些是强制上升,剩下的任意。

假设分出来的块大小为 a i a_i ai,则一行方案数为 n ! ∏ a i ! \frac{n!}{\prod a_i!} ai!n!

考虑DP,设 f [ i ] [ j ] f[i][j] f[i][j]表示以 i k ik ik结尾的,前面划分了 j j j块的方案数,显然有转移:

f [ i ] [ j ] = ∑ k = 0 i − 1 f [ k ] [ j − 1 ] l e n ! r f[i][j]=\sum_{k=0}^{i-1}\frac{f[k][j-1]}{len!^r} f[i][j]=k=0i1len!rf[k][j1]

其中 l e n len len是这次划分的块长。

第二位可以直接和容斥系数压在一起,单次DP复杂度 O ( ( n k ) 2 ) O((\frac{n}{k})^2) O((kn)2)

总复杂度积个分知道是 O ( n 2 ) O(n^2) O(n2)

有一种生成函数做法,需要MTT,不想写,但是可以用Karatsuba优化到 O ( n log ⁡ 2 3 ) O(n^{\log_23}) O(nlog23)


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

cs int mod=1e9+123;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
	return res;
}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}

cs int N=5e4+7;

int n=5e4,m;

int fac[N],ifac[N],b[N],f[N];

inline void init_inv(){
	fac[0]=fac[1]=ifac[0]=ifac[1]=1;
	for(int re i=2;i<=n;++i)fac[i]=mul(fac[i-1],i);
	ifac[n]=power(fac[n],mod-2);
	for(int re i=n-1;~i;--i)ifac[i]=mul(ifac[i+1],i+1);
	for(int re i=2;i<=n;++i)
	fac[i]=power(fac[i],n),ifac[i]=power(ifac[i],n);
}

inline int calc(){
	f[0]=fac[n];
	memset(f+1,0,sizeof(int)*m);
	for(int re i=1;i<=m;++i){
		for(int re j=i-1,l=0;~j;--j){
			l+=b[j+1];
			(i^j)&1?Inc(f[i],mul(f[j],ifac[l])):Dec(f[i],mul(f[j],ifac[l]));
		}
	}
	return f[m];
}

signed main(){
	init_inv();int ans=0;
	for(int re i=1;i<=n;++i){m=0;
		for(int re j=n;j;j-=b[m])b[++m]=std::min(i,j);
		Inc(ans,calc());
	}
	std::cout<<ans<<"\n";
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值