1. 程式人生 > >簡單數論練習題OJ

簡單數論練習題OJ

啊,我怎麼這麼菜,死啦,高角度都不會了

A、錯排問題

時間限制: 1 Sec  記憶體限制: 128 MB

題目描述

如果一個n的全排列(a1,a2,...,an)滿足對於所有i,ai = i,則該排 列稱為 n 的一個錯排。你的任務是計算 n 的錯排一共有多少個。 

輸入

輸入一個正整數 n。 n ≤ 1000 

輸出

輸出一個正整數,代表 n 的錯排的個數。 

樣例輸入

3

樣例輸出

2

(懶得看的大牛公式就在這兒: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。 

上面寫的很詳細,開高精就好了

#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;
}