• 「JOISC 2018 Day 1」帐篷


    题意:(n)(m)列的矩阵((n,m<=3000)),在格子里搭一些帐篷.每座帐篷必须占据刚好一个格子.没有两座帐篷会占据同一个格子.每座帐篷在东、南、西、北四个方向之一有一个出入口.帐篷的出入口朝向必须满足以下条件:同一行或同一列的两个帐篷的出入口要相对(例如同一行的两个帐篷,左(西)边的帐篷出入口对着右(东)边,右边的对着左边).求在矩阵中至少放一个帐篷的方案数.

    分析:根据这个放帐篷的条件,我们可以发现,我们放的帐篷只可能是如下三种情况之一:

    第一种:一个帐篷占据了一行和一列,那么这个帐篷的出入口的朝向是四个方向任意一个;

    第二种:两个帐篷占据了一行和两列;

    第三种:两个帐篷占据了两行和一列;

    (f[i][j])表示当前放第i行和第j列的方案数.

    对应第一种情况,(f[i][j]+=f[i-1][j-1]*4*C_j^1)

    对应第二种情况,(f[i][j]+=f[i-1][j-2]*C_j^2)

    对应第三种情况,(f[i][j]+=f[i-2][j-1]*C_{i-1}^1*C_j^1)

    解释一下状态转移方程中可能产生疑问的地方.

    为什么情况二不要乘上选哪一行的情况?因为我们的外层循环默认了一个填帐篷的顺序,即从第1行到第n行,然后情况二我们默认就是填的就是第i行.

    为什么情况三要乘上选哪一行?因为我们一次要填两行,而这两行不一定是相邻的两行.所以我们只确定了要填第i行,然后要在前i-1行中再选一行来填.

    这个只是f数组的状态转移,我们考虑如何统计答案?注意到题目只是要保证至少填一个帐篷即可,那么必然有些方案会有一些空行和空列没有填,所以(ans=sum_{i=1}^nsum_{j=1}^mf[i][j]*C_n^i*C_m^j)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int mod=1e9+7;
    const int N=3005;
    ll ans,jc[N],inv[N],f[N][N];
    inline ll ksm(ll a,int b){
    	ll cnt=1;
    	while(b){
    		if(b&1)cnt=(1ll*cnt*a)%mod;
    		a=(1ll*a*a)%mod;
    		b>>=1;
    	}
    	return cnt;
    }
    inline ll C(int n,int m){return (1ll*jc[n]*inv[n-m]%mod*inv[m])%mod;}
    int main(){
    	jc[0]=1;for(int i=1;i<=3000;++i)jc[i]=(1ll*jc[i-1]*i)%mod;
    	inv[3000]=ksm(jc[3000],mod-2);
    	for(int i=3000;i>=1;--i)inv[i-1]=(1ll*inv[i]*i)%mod;
    	int n=read(),m=read();f[0][0]=1;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j){
    			f[i][j]=(f[i][j]+((1ll*f[i-1][j-1]*j)%mod*4)%mod)%mod;
    			if(j>=2)f[i][j]=(f[i][j]+(1ll*f[i-1][j-2]*C(j,2))%mod)%mod;
    			if(i>=2)f[i][j]=(f[i][j]+((1ll*f[i-2][j-1]*(i-1))%mod*j)%mod)%mod;
    		}
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			ans=(ans+(1ll*f[i][j]*C(n,i)%mod*C(m,j))%mod)%mod;
    	printf("%lld
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    生活网站
    input 输入值的监听 禁止输入特殊字符
    jq 插件分享
    css3 特效分享
    sharepoint---RBS回收站清空设置
    &&和||
    DataTable得到某行某列的值
    后台刷新当前页面和弹出对话框跳转页面
    c#.netGr idView1在div不局中
    DIV UL LI
  • 原文地址:https://www.cnblogs.com/PPXppx/p/11372680.html
Copyright © 2020-2023  润新知