简单数论练习题OJ

啊,我怎么这么菜,死啦,高角度都不会了

A、错排问题

时间限制: 1 Sec  内存限制: 128 MB

题目描述

如果一个n的全排列(a1,a2,...,an)满足对于所有i,ai = i,则该排 列称为 n 的一个错排。你的任务是计算 n 的错排一共有多少个。 

输入

输入一个正整数 n。 n ≤ 1000 

输出

输出一个正整数,代表 n 的错排的个数。 

样例输入

3

样例输出

2

 

错排问题递推公式:见https://blog.csdn.net/kk303/article/details/7372555

(懒得看的大牛公式就在这儿:f(n) = (n-1)[f(n-2)+f(n-1)] (n>2) 

套一个高精度

#include<cstdio>
#include<iostream>
using namespace std;
  
const int N=3e6+5;
int n,len[4],d1[N],d2[N],d0[N],d4[N],c[N];
  
void jia()
{
    for(int i=1;i<=max(len[1],len[2]);i++)
    {
        len[0]++; 
        int t=d0[len[0]]+d1[i]+d2[i];
        d0[len[0]]=t%10;
        d0[len[0]+1]+=t/10;
    }
    while(d0[len[0]+1]) 
        len[0]++,d0[len[0]+1]+=d0[len[0]]/10,
        d0[len[0]]=d0[len[0]]%10;
}
  
void cheng()
{
    for(int i=1;i<=len[0];i++)
    {
        for(int j=1;j<=len[4];j++)
            c[i+j]+=(c[i+j-1]+d0[i]*d4[j])/10,
            c[i+j-1]=(c[i+j-1]+d0[i]*d4[j])%10;
    }
    int lenc=len[0]+len[4]-1;
    while(c[lenc+1]) 
        lenc++,c[lenc+1]=c[lenc]/10,c[lenc]%=10;
    len[0]=lenc;
    for(int i=1;i<=len[0];i++)
        d0[i]=c[i];
}
int main()
{
    scanf("%d",&n);
    len[0]=len[1]=1;    
    d1[1]=0,d0[1]=1;
    for(int i=3;i<=n;i++)
    {
        len[2]=len[1];
        for(int j=1;j<=len[2];j++)
            d2[j]=d1[j];
        len[1]=len[0]; len[0]=0;
        for(int j=1;j<=len[1];j++)
            d1[j]=d0[j],c[j]=d0[j]=0;
        jia();
        len[4]=0; int x=i-1;
        while(x) d4[++len[4]]=x%10,x/=10;
        cheng();
    }
    for(int i=len[0];i>0;i--)
        printf("%d",d0[i]);
    return 0;
} 

 

B、 线性不等式的解

时间限制: 1 Sec  内存限制: 128 MB

题目描述

求出线性不等式 x1 + x2 + ··· + xn ≤ b 的非负整数解的个数。 

输入

一行两个正整数 n, b,含义如题中所述。 

输出

一行一个正整数,代表解的个数,只需输出该数除 1000003 的余数。 

样例输入

1 1

样例输出

2

提示

n ≤ 50000, b ≤ 50000 

转化为线性等式处理。注意时间复杂度。 

 

因为是不等式,可以加入一个数xn+1

=>x1+x2+……+xn+1==b

因为x可能为0,所以全部加1

(x1+1)+(x2+1)+……+(xn+1+1)==b+n+1

插板法:C(n,n+b)

维护一下即可

#include<cstdio>
#define ll long long
const int p=1000003;
using namespace std;
  
int n,m;
ll ans;
  
int ksm(ll a,int b)
{
    ll ret=1;
    while(b)
    {
        if(b&1) ret=ret*a%p;
        a=a*a%p; b>>=1;
    }
    return ret;
}
int main()
{
    scanf("%d%d",&n,&m);
    m+=n; ans=1;
    for(int i=m-n+1;i<=m;i++) ans=ans*i%p;
    for(int i=1;i<=n;i++) ans=ans*ksm(i,p-2)%p; //费马小定理
    printf("%d\n",ans);
    return 0;
} 

 

C、 数的计数

时间限制: 1 Sec  内存限制: 128 MB

题目描述

已知素数 p1, p2, . . . , pn,请求出 [1, m] 中至少能被一个 pi 整除的数的 个数。对于所有i=j有pi =pj,且p1 ·p2 ·...·pn ≤231 −1。 

输入

第一行为两个整数 n,m。 接下来一行有 n 个整数,代表 pi。 

输出

一个整数,代表至少能被一个 pi 整除的数的个数。 

样例输入

1 10 5

样例输出

2

提示

n≤20,m≤231 −1 

 

简单的容斥原理

#include<cstdio>
#define ll long long
using namespace std;
  
const int N=100;
int n,m,a[N],num[2000005],lg[2000005];
ll f[2000005],ans;
  
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    lg[0]=-1;
    for(int i=1;i<1<<n;i++) lg[i]=lg[i>>1]+1;
    f[0]=1;
    for(int S=1;S<1<<n;S++)
    {
        f[S]=f[S-(S&-S)]*a[lg[S&-S]+1];   //状态为S的数的成绩
        if(f[S]>m) f[S]=m+1;
    }
    for(int i=1;i<1<<n;i++) num[i]=num[i-(i&-i)]+1;
    ans=0;
    for(int i=1;i<1<<n;i++) 
        if(num[i]&1) ans+=m/f[i]; else ans-=m/f[i];
    printf("%d\n",ans);
    return 0;
} 

D、盒子

时间限制: 1 Sec  内存限制: 128 MB

题目描述

N 个盒子排成一行(1<=N<=20)。你有 A 个红球和 B 个蓝球。0 <= A <= 15, 0 <= B <= 15。球除了颜色没有任何区别。你可以将球放进 盒子。一个盒子可以同时放进两种球,也可以只放一种,也可以空着。球 不必全部放入盒子中。编程计算有多少种放置球的方法。 

输入

一行,N,A,B,用空格分开。 

输出

一行,输出放置方案总数。

样例输入

2 1 1

样例输出

9

 

记忆化搜索

记得开unsigned long long ,20 15 15时会爆

#include<cstdio>
#include<iostream>
#define ll unsigned long long
using namespace std;
  
int n,a,b;
ll f[25][20][20];
ll dfs(int n,int a,int b)
{
    if(f[n][a][b]) return f[n][a][b];
    if(!n) return 1;
    f[n][a][b]=dfs(n-1,a,b);
    for(int i=0;i<a;i++)
        f[n][a][b]+=dfs(n-1,i,b);
    for(int i=0;i<b;i++)
        f[n][a][b]+=dfs(n-1,a,i);
    for(int i=0;i<a;i++)
        for(int j=0;j<b;j++)
            f[n][a][b]+=dfs(n-1,i,j);
    return f[n][a][b];
}
int main()
{
    scanf("%d%d%d",&n,&a,&b);
    cout<<dfs(n,a,b);
    return 0;
}

 

 E、全排列

时间限制: 1 Sec  内存限制: 128 MB

题目描述

输入两个自然数m,n,1 ≤ n ≤ 100,1 ≤ m ≤ n!输出n个数的第m 种全排列(按字典序排序后的第 m 个全排列)。 

输入

在一行中输入 n,m 。 

输出

一个数列, 即 n 个数的第 m 种排列。每两个数之间空 1 格。 

样例输入

3 2

样例输出

1 3 2

 

看代码(菜鸡不会解释)

#include<cstdio>
#include<cstring>
using namespace std;
  
const int N=1e6+5;
int n,len1,a[N],len2,b[N],len3,d[N],len,c[N];
char s[N];
bool v[N];
  
void cheng()
{
    for(int i=1;i<=len2;i++)
        for(int j=1;j<=len3;j++)
            c[i+j]+=(c[i+j-1]+b[i]*d[j])/10,
            c[i+j-1]=(c[i+j-1]+b[i]*d[j])%10;
    len=len2+len3-1;
    while(c[len+1])
        len++,c[len+1]=c[len]/10,c[len]%=10;
    len2=len;
    for(int i=1;i<=len2;i++) b[i]=c[i];          
}
  
inline bool compate()
{
    if(len2>len1) return 0;
    if(len2<len1) return 1;
    if(len2==len1)
        for(int j=len1;j;j--)
            if(a[j]>b[j]) return 1;
                else if(a[j]<b[j]) return 0;
    return 1;
}
  
void jian()
{
    for(int i=1;i<=len2;i++)
    {
        a[i]-=b[i];
        if(a[i]<0) a[i]+=10,a[i+1]--; 
    }   
    for(int i=len2+1;i<=len1;i++)
        if(a[i]<0) a[i]+=10,a[i+1]--;
    while(len1&&!a[len1]) len1--;
}
  
int div()
{
    int ret=0;
    while(compate()) jian(),ret++;
    return ret;  
}
  
int main()
{
    scanf("%d%s",&n,s+1);
    len1=strlen(s+1);
    for(int i=len1;i;i--)
        a[i]=s[len1-i+1]-'0';
    a[1]--;
    for(int i=1;i<=len1;i++)
        if(a[i]<0) a[i]+=10,a[i+1]--;
    while(!a[len1]) len1--;
    for(int i=1;i<=n;i++)
    {
        len2=1; b[1]=1;
        for(int j=1;j<=n-i;j++)
        {
            int x=j;
            len3=0;
            while(x)
                d[++len3]=x%10,x/=10;
            cheng();
            for(int k=1;k<=len2;k++) c[k]=0;
        }
        int x=div()+1,y=0;
        for(int j=1;j<=n;j++)
            if(!v[j]) 
            {
                y=j,x--;
                if(!x) break;
            }
        v[y]=1;
        printf("%d ",y);
    }
    return 0; 
}

 

F、第二类 Stirling 数

时间限制: 1 Sec  内存限制: 128 MB

题目描述

第二类 Stirling 数 S(n, m) 是指将 n 个有区别的球放入 m 个无区别的 盒子的方案数,不允许有空盒。 

 

输入

一行两个数 n,m。 

 

输出

输出 S(n, m)。 

 

样例输入

3 1

样例输出

1

提示

n ≤ 100, m ≤ 100。 

 

https://blog.csdn.net/zhn_666/article/details/78215809

上面写的很详细,开高精就好了

#include<cstdio>
#include<iostream>
using namespace std;
 
const int N=105;
int n,m;
 
struct NA{
    int x[N<<1],len;
}f[N][N],a;
 
NA cheng(NA x,int y)
{
    for(int i=1;i<=x.len;i++)
        x.x[i]*=y;
    for(int i=1;i<=x.len;i++)
        x.x[i+1]+=x.x[i]/10,
        x.x[i]%=10;
    while(x.x[x.len+1]) 
        x.len++,
        x.x[x.len+1]+=x.x[x.len]/10,
        x.x[x.len]%=10;
    return x;
}
 
NA jia(NA x,NA y)
{
    for(int i=1;i<=max(x.len,y.len);i++)
    {
        x.x[i]+=y.x[i];
        if(x.x[i]>=10) x.x[i]%=10,x.x[i+1]++;
    }
    x.len=max(x.len,y.len);
    while(x.x[x.len+1]) x.len++;
    return x;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)    
        f[i][1].x[1]=f[i][1].len=1;
    for(int i=2;i<=n;i++)
        for(int j=2;j<=m;j++)
        {
            a=cheng(f[i-1][j],j);
            f[i][j]=jia(a,f[i-1][j-1]);
        }
    for(int i=f[n][m].len;i;i--)
        printf("%d",f[n][m].x[i]);
    return 0;
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值