(啊,我怎么这么菜,死啦,高角度都不会了)
A、错排问题
时间限制: 1 Sec 内存限制: 128 MB
题目描述
如果一个n的全排列(a1,a2,...,an)满足对于所有i,ai = i,则该排 列称为 n 的一个错排。你的任务是计算 n 的错排一共有多少个。
输入
输入一个正整数 n。 n ≤ 1000
输出
输出一个正整数,代表 n 的错排的个数。
样例输入
3
样例输出
2
错排问题递推公式:见https://blog.youkuaiyun.com/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.youkuaiyun.com/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;
}