B

Time Limit: 1000 ms Memory Limit: 512 MB

Description

img

img


Solution

  • 30%

  大力\(n^4\)dp一波,枚举\(n\)\(f[i][j][k]\)表示前\(i\)列,有\(j\)行有1个1,有\(k\)行有2个1,那么乘个组合数转移一下就好了

  (这种有点鬼畜的状态表示方式类似【bzoj1079】着色方案)

  

  • 40%

  啊。。。反正。。只读一个数嘛然后\(n\)的范围很小单次求\(n^3\)跑500也不慢反正是场上那就。。。

  打表啊qwq

  

  • 70%不会qwq

  

  • 100%

  OEIS搜前四项

  如果将同一行和同一列的\(1\)用边连起来,一种方案就可以变成。。。很多个环(好的光是这步就想不到了qwq)

  那么我们考虑从这个方面入手,\(f_n\)表示\(n*n\)的矩阵的答案,那么我们枚举矩阵的第一行的\(1\)所在的环的大小,可以得到这样的递推式:

\[f_n=\sum\limits_{i=2}^{n}A_i \cdot \binom n i \cdot \binom {n-1}{i-1}\cdot f_{n-i} \]

  (注意,行与行之间、列与列之间在这题里面其实没有区别,所以可以这么考虑)

  其中\(A_i\)表示一个\(i*i\)的矩阵中只有一个环的方案数,\(\binom n i\)枚举是原矩阵的哪\(i\)列,\(\binom {n-1}{i-1}\)枚举是原矩阵的哪\(i-1\)行(因为第一行已经确定是要选的了)

  然后有一个结论,\(A_i=\frac{n!(n-1)!}{2}\),然后题解里面有这样的一个比较好的解释嗯

  那么接下来就是化简式子了:

\[\begin{aligned} f_n&=\sum\limits_{i=2}^{n}A_i \cdot \binom n i \cdot \binom {n-1}{i-1}\cdot f_{n-i}\\ &=\sum\limits_{i=2}^{n}\frac{n!(n-1)!}{2}\binom n i \binom {n-1}{i-1}f_{n-i}\\ &=\sum\limits_{i=2}^{n}\frac{i!(i-1)!}{2}\frac{n!}{i!(n-i)!}\frac{(n-1)!}{(i-1)!(n-i)!}f_{n-i}\\ &=\frac{n!(n-1)!}{2}\sum\limits_{i=2}^{n}\frac{f_{n-i}}{((n-i)!)^2}\\ \end{aligned} \]

  然后我们令\(g_x=\frac{f_x}{(x!)^2}\),那么

\[f_n=\frac{n!(n-1)!}{2}\sum\limits_{i=2}^{n}g_{n-i} \]

  然后我们化一下就可以得到:

\[\begin{aligned} &g_n=\frac{1}{2n}\sum\limits_{i=2}^{n}g_{n-i}\\ &f_n=\frac{n!(n-1)!}{2}\sum\limits_{i=2}^{n}g_{n-i}\\ \end{aligned} \]

  好的然后会发现\(g\)的话直接前缀和优化一下就可以做到\(O(n)\)了,\(g\)求出来之后,\(f\)的值也可以在线性的时间内求出,那么就可以在\(O(n)\)的时间内解决啦

  (然而。。。)

  然而有个东西是\(\frac{1}{2n}\),这个东西如果直接快速幂爆搞就会十分愉快地变成\(nlogn\)就会\(T\)

​  所以我们要线性求逆元(这个东西。。晚点整理好了先贴篇blog)

  最后还有一个小问题就是,\(g_0\)\(1\),具体为什么。。(我也真的不是很知道qwq)

​  

  题外话:由于我写的太挫需要各种奇怪的常数优化才能。。卡过去qwq(然而本地没开O2真的是0.7s啊!!为什么上去就那么慢了啊qwq)什么不要用long long而是用int啊少点模啊之类的。。。%yxq巨佬10行AC

  (题外话的题外话:据yxq巨佬所说这题和GDSOI2015的循环排插很像。。)

  代码大概长这样

#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
const int MAXN=1e7+10,MOD=998244353;
int fac[MAXN],invfac[MAXN];
int g[MAXN],sum[MAXN],inv[MAXN*2];
int n,m;
void prework(int n);
int ksm(int x,int y);
int sqr(int x){return 1LL*x*x%MOD;}

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	ll ans=1;
	int inv2=ksm(2,MOD-2);
	scanf("%d",&n);
	prework(n);
	g[1]=0; g[2]=sqr(invfac[2])%MOD;
	sum[1]=1; sum[2]=sum[1]+g[2];
	for (int i=3;i<=n;++i){
		g[i]=1LL*inv[i]*inv2%MOD*sum[i-2]%MOD;
		sum[i]=(sum[i-1]+g[i])%MOD;
	}
	for (int i=3;i<=n;++i){
		ans=ans+1LL*fac[i]*fac[i-1]%MOD*inv2%MOD*sum[i-2]%MOD;
		if (ans>MOD) ans-=MOD;
	}
	printf("%d\n",ans);
}

void prework(int n){
	fac[0]=1;
	for (int i=1;i<=n;++i) fac[i]=1LL*fac[i-1]*i%MOD;
	invfac[n]=ksm(fac[n],MOD-2);
	for (int i=n-1;i>=0;--i) invfac[i]=1LL*invfac[i+1]*(i+1)%MOD;
	inv[1]=1;
	for (int i=2;i<=n;++i)
		inv[i]=1LL*(MOD-(MOD/i))*inv[MOD%i]%MOD;
}

int ksm(int x,int y){
	int ret=1,base=x;
	for (;y;y>>=1,base=1LL*base*base%MOD)
		if (y&1) ret=1LL*ret*base%MOD;
	return ret;
}
posted @ 2018-04-17 20:30  yoyoball  阅读(133)  评论(0编辑  收藏  举报