簡單數論練習題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;
}