zoukankan      html  css  js  c++  java
  • 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=sumlimits_{i=2}^{n}A_i cdot inom n i cdot inom {n-1}{i-1}cdot f_{n-i} ]

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

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

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

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

    [egin{aligned} f_n&=sumlimits_{i=2}^{n}A_i cdot inom n i cdot inom {n-1}{i-1}cdot f_{n-i}\ &=sumlimits_{i=2}^{n}frac{n!(n-1)!}{2}inom n i inom {n-1}{i-1}f_{n-i}\ &=sumlimits_{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}sumlimits_{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}sumlimits_{i=2}^{n}g_{n-i} ]

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

    [egin{aligned} &g_n=frac{1}{2n}sumlimits_{i=2}^{n}g_{n-i}\ &f_n=frac{n!(n-1)!}{2}sumlimits_{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
    ",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;
    }
    
  • 相关阅读:
    ComboBox.DoubleClick事件
    mktime 夏令时
    STL String的使用[转]
    加在电源后至进入操作系统前的计算机的行为
    C语言数据类型大小分析(基于VC2005编译器)
    linux线程同步之条件变量
    windows 下架设svn服务器(转载+修改) (非利用Google项目托管)
    浅尝《Windows核心编程》之内核对象
    C——数组与指针
    如何用U盘做系统启动盘WINPE 并且 利用WINPE安装Ghost
  • 原文地址:https://www.cnblogs.com/yoyoball/p/8869840.html
Copyright © 2011-2022 走看看