一些数学知识补充

1 分数取模

需要对一个分数 p q \frac{p}{q} qp取模时,若 g c d ( q , m ) = 1 gcd(q,m)=1 gcd(q,m)=1
p q 1 p × q m 2 ( m o d m ) p*q^{-1} \equiv p\times q^{m-2}\pmod m pq1p×qm2(modm)

第二场A题,答案需令 1 n 1 \frac{1}{n-1} n11 1 0 9 + 7 10^9+7 109+7 取模
故代码:

#include<bits/stdc++.h>
using namespace std;

const int mod=1e9+7;
long long qpow(long long a,long long b){
    long long res=1;
    while(b){
        if(b & 1){
            res=res*a%mod;
        }
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int t;
int main(){
    cin>>t;
    long long ans=1;
    while(t--){
        long long n,m;
        cin>>n>>m;
        if(n==1)
            ans*=1;
        else if(m==0)
            ans=0;
        else{
            ans=ans*qpow(n-1,mod-2)%mod;
        }
        cout<<ans<<endl;
    }
    return 0;
}

2 数论知识补充

ab互为乘法逆元
a b 1 ( m o d f ) ab\equiv1\pmod f ab1(modf)
由于扩展欧几里得可解方程 a x + b y = 1 ax+by=1 ax+by=1
故可将原式变形 a x + f y = 1 ax+fy=1 ax+fy=1,x为a的乘法逆元

费马小定理(求分数逆元)
如果p为质数,则 a p a a^p-a apa是p的倍数
也可写作 a p 1 1 ( m o d p ) a^{p-1}\equiv 1 \pmod p ap11(modp)

知乎上讲欧拉定理的专栏

扩展欧几里得
一层层写出辗转相除法的表达式,外层的式子不断代入内层,最终可得gcd的用a和b的线性表示
a x 1 + b y 1 = b x 2 + ( a &VeryThinSpace; m o d &VeryThinSpace; b ) y 2 ax_1+by_1=bx_2+(a\bmod b)y_2 ax1+by1=bx2+(amodb)y2
a x 1 + b y 1 = b x 2 + ( a ( a / b ) b ) y 2 = a y 2 + b x 2 ( a / b ) b y 2 ax_1+by_1=bx_2+(a-(a/b)*b)y_2=ay_2+bx_2-(a/b)*by_2 ax1+by1=bx2+(a(a/b)b)y2=ay2+bx2(a/b)by2

int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {            //1的情况
        x=1;
        y=0;
        return a;
    }
    int r=exgcd(b, a%b, x, y);
    int t=y;
    y=x-(a/b)*y;     //2的情况
    x=t;
    return r;
}


3 思维+动规(形式上)

题目链接
  大概就是,对于一个由A,B组成的字符串,要求能拆分成n个AB子串和m个BA子串。给定n和m,求出合法字符串的种数。

对于一个合法串,我们可以令前n个A构成AB子串,前m个B构成BA子串;反过来,能够满足这两个条件的也是合法串

用前缀思路,无法直接求某一长度字符串的种类数,可不断更新求出某一(n,m)下的合法的前缀数。令dp[i][j]表示i个A,j个B时合法前缀的种类数

前缀的合法性为:譬如向一个前缀尾部添加A,如果此时i<n,自然可行,因为AB中的A要尽可能早地出场;而i>=n时,再添加A只能作为BA中的A,此时如果前面有足够的B来搭配,也可行,即i<n+j,其中有贪心的思想,我们认为此时可以认为之前出现的B均作为BA中的B;若不是,则说明B的数量已经超过BA的需求量,即已经开始为AB提供B元素了。此时其实可以将剩余的A和B以任意顺序附着在后面。

代码:

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int dp[2001][2001];
int n,m;
int main(){
    while(cin>>n>>m){
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int i=0;i<=n+m;i++){
            for(int j=0;j<=n+m;j++){
                if(i<=n+j&&i>0) dp[i][j]=(dp[i-1][j]+dp[i][j])%mod;
                if(j<=m+i&&j>0) dp[i][j]=(dp[i][j-1]+dp[i][j])%mod;
            }
        }
        cout<<dp[n+m][n+m]<<endl;
    }
    return 0;
}

另:超时几次结果发现是每次memset整个数组太耗时,有功夫研究一下它和遍历初始化方法的速度差异。

全部评论

相关推荐

10-09 22:05
666 C++
找到工作就狠狠玩CSGO:报联合国演讲,报电子烟设计与制造
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务