zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:visit(组合数学+exLucas+CRT)

    题目传送门(内部题6)


    输入格式

    第一行包含两个整数$T$,$MOD$;
    第二行包含两个整数$n$,$m$,表示$dirty$房子的位置。


    输出格式

    一行一个整数,表示对$MOD$取模之后的答案。


    样例

    样例输入:

    4 10

    2 2

    样例输出:

    6


    数据范围与提示

    对于$30\%$的数据,$T leqslant 100$;
    对于另外$30\%$的数据,$MOD$为质数;
    对于全部数据,$1 leqslant T leqslant 100,000;-T leqslant n,m leqslant T;1 leqslant MOD leqslant {10}^9+7$,MOD为若干互不相同的质数的乘积。


    题解

    $30\%$算法:

    考虑$DP$,定义$dp[i][j][k]$表示在时间为$i$的时候走到了$(j,k)$,$pure$只能从上下左右四个方向走过来,那么很容易就能写出$dp$式子:

    $dp[i][j][k]=dp[i-1][j-1][k]+dp[i-1][j+1][k]+dp[i-1][j][k-1]+dp[i-1][j][k+1]$。

    但是这并不能轻松的拿到$30$分,需要注意的是,$n$和$m$有可能是负数,但是我们有发现,根据出租车几何,不管$(n,m)$这个点在第几象限,显然我们都能把它当成在第一象限来解决,用$abs$函数吧$n$和$m$都转化为正数即可。

    而且$DP$的边界要搞清,因为$pure$最多会往上下左右四个方向走$T$步,所以$dp$的边界要设成$-Tsim T$。

    当然$dp$式子中也要$[j][k]$两维也要$+100$来处理负数的情况。

    总之,$30\%$的算法也不好打。

    时间复杂度:$O(n^3)$。

    期望得分:$30$分。

    实际得分:$30$分。

    $60\%$算法:

    对于如此之大的数据范围,显然我们不能在用哪怕一个二维数组来存了。

    那么我们来考虑转化这个问题,因为我们最终肯定要向右走$n$步,向上走$m$步,然后剩下的$T-n-m$步用来浪费到向右走再走回来和向上走再走回来。

    所以可以发现:$T=n+m+2(x+y)$(式中x表示向右走的多余步数,y表示向上走的多余步数)。

    我们还可以发现,如果$n+m<T$或者是$(T-n-m)mod2 eq0$时都无解。

    而且,如果我们确定了$x$和$y$的其中一个,我们就能确定另一个。

    在来考虑这样一个问题:

    如果有$n$个物品,每个物品有$a_n$个,那么本质不同的排列数即为:

    $frac{k!}{a_1 imes a_2 imes... imes a_{n-1} imes a_n}$

    那么这道题的答案即为:

    $frac{T!}{x! imes y! imes (n+x)! imes (m+y)!}$

    然后因为$mod$是一个质数,所以我们可以直接用$Lucas$定理和阶乘逆元轻松解决。

    时间复杂度:$O(nlog n)$。

    空间复杂度:$T$。

    期望得分:$60$分。

    $100\%$算法:

    我们发现$mod$并不是一个质数,但是$mod$是若干个互不相同质数的乘积,于是想到了中国剩余定理($CRT$),先将$mod$分解,然后计算对于没一个分解出来的模数计算出来的结果,$Lucas$定理即可轻松解决,最后再用$CRT$合并即可。


    代码时刻

    $30\%$算法:

    #include<bits/stdc++.h>
    using namespace std;
    int t,n,m,mod;
    long long dp[101][202][202];
    int main()
    {
        scanf("%d%d%d%d",&t,&mod,&n,&m);
        n=abs(n);
        m=abs(m);//变为正数
        dp[0][100][100]=1;
        for(int i=1;i<=t;i++)
            for(int j=-t;j<=t;j++)
                for(int k=-t;k<=t;k++)
                    dp[i][j+100][k+100]=(
                    dp[i][j+100][k+100]+
                    dp[i-1][j+1+100][k+100]+
                    dp[i-1][j+100][k+1+100]+
                    dp[i-1][j-1+100][k+100]+
                    dp[i-1][j+100][k-1+100])%mod;//状态转移
        printf("%lld",dp[t][n+100][m+100]);
        return 0;
    }
    

    $100\%$算法

    #include<bits/stdc++.h>
    using namespace std;
    int t,mod,n,m;
    int wzc[500];
    long long fac[200000],rec[500];
    long long ans;
    long long qpow(long long x,long long y,long long p)
    {
    	long long res=1;
    	while(y)
    	{
    		if(y%2)res=(res*x)%p;
    		y>>=1;
    		x=(x*x)%p;
    	}
    	return res;
    }
    long long get_C(long long x,long long y,long long p)
    {
    	if(x<y)return 0;
    	return fac[x]%p*qpow(fac[x-y]*fac[y]%p,p-2,p)%p;
    }
    long long lucas(long long x,long long y,long long p)//Lucas定理
    {
    	if(!y)return 1;
    	return (get_C(x%p,y%p,p)*lucas(x/p,y/p,p))%p;
    }
    void pre_mod(int x)//分解模数
    {
    	for(int i=2;i<=sqrt(mod);i++)
    		if(!(x%i))
    		{
    			wzc[++wzc[0]]=i;
    			x/=i;
    		}
    	if(x>1)wzc[++wzc[0]]=x;
    }
    void pre_work(int x)
    {
    	int maxn=min(x-1,t);
    	fac[0]=1;
    	for(int i=1;i<=maxn;i++)
    		fac[i]=fac[i-1]*i%x;//预处理阶乘
    }	
    int main()
    {
    	scanf("%d%d%d%d",&t,&mod,&n,&m);
    	n=abs(n);
    	m=abs(m);
    	pre_mod(mod);
    	for(int i=1;i<=wzc[0];i++)
    	{
    		pre_work(wzc[i]);
    		for(int j=n;j<=t-m;j+=2)
    			rec[i]=(rec[i]+lucas(t,j,wzc[i])*lucas(j,(j-n)/2,wzc[i])%mod*lucas(t-j,(t-j-m)/2,wzc[i])%mod)%mod;
    	}
    	for(int i=1;i<=wzc[0];i++)
    		ans=(ans+mod/wzc[i]*rec[i]%mod*qpow(mod/wzc[i],wzc[i]-2,wzc[i])%mod)%mod;//CRT
    	printf("%lld",ans);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    如何改变checkbox的样式
    EChart.js 简单入门
    Javascript异步编程的4种方法
    手写手机网站
    Handlebars的基本用法
    装饰器
    using Newtonsoft.Json;
    chrome插件学习笔记
    绩效考核如何快速评分
    wx jssdk
  • 原文地址:https://www.cnblogs.com/wzc521/p/11230479.html
Copyright © 2011-2022 走看看