NOIP提高组模拟赛夜间版1
由于上午考试出了其他大佬打过的原题,所以晚上加试
A. 中国象棋
一看题面,状压DP!一看数据范围。。。
实际可以发现,每行每列最多放两个炮,统计方案数的话具体在哪里是无所谓的
定义f[i][j][k]表示前i行有j个位置放了1个棋子,k个位置一个棋子都没放的方案数
初始状态f[0][0][m]=1;
分类讨论
不放
放1个棋子在之前没放过的列,有种放法
放1个棋子在之前放过一个的列,有种放法
放2个棋子在之前没放过的列,有种放法
放2个棋子在之前放过1个的列,有种放法
放1个棋子在之前放过1个的列,1个棋子在之前没放过的列,有种放法
记得取模,去掉减法减出负数的越界
最后答案对求和
#include<cstdio>
#include<algorithm>
using namespace std;
const int mod=9999973;
int n,m;
long long f[105][105][105];
int main(){
scanf("%d%d",&n,&m);
f[0][0][m]=1;//f[i][j][k] i行j个1k个0
for(int i=1;i<=n;++i){
for(int j=0;j<=m;++j){
for(int k=0;k<=m;++k){
f[i][j][k]+=f[i-1][j][k];//不放
if(j)f[i][j][k]+=f[i-1][j-1][k+1]*(k+1);//放一个在没放过的列
f[i][j][k]+=f[i-1][j+1][k]*(j+1); //放一个在放过1个的列
if(j>=2)f[i][j][k]+=f[i-1][j-2][k+2]*(k+2)*(k+1)/2;//放两个在没放过的列
f[i][j][k]+=f[i-1][j+2][k]*(j+2)*(j+1)/2;//放两个在放过一个的列
f[i][j][k]+=f[i-1][j][k+1]*j*(k+1);//放一个在没放过的列,1个在放过1个的列
f[i][j][k]=f[i][j][k]%mod;
}
}
}
long long ans=0;
for(int i=0;i<=m;++i)
for(int j=0;j<=m;++j)
ans=(ans+f[n][i][j])%mod;
printf("%lld\n",ans);
return 0;
}
B. 奇妙的 Fibonacci
奇妙奇妙,太奇妙了,大爱数论
感觉会敲latex到吐,提前祭奠一下
先说简单不用latex的
考场直接打表,先敲个暴力,然后换用不同放法妄图探究性质,然后在输出第i项Fibonacci能整除第几项时发现了规律打表万岁,除了第二项特殊,其他第i项能整除第项,看到这个自然想到了埃氏筛,
yy了一下复杂度感觉能过,打完跑路,喜提TLE。线性筛也不会求这玩意啊
再来严谨的证明
同理可继续推下去有
即 ......................
......................
(因为(1))
(因为(2))
上式与类比
可以得出,对于相同n,m上述两个整除关系同时成立,也就是说
这就是我打表打出的那个性质,也是解决问题的第一个关键点
经过这样的证明,我们可以将原问题转化为
对于i求i的约数个数和i约数的平方和
如何解决这个问题
考虑使用埃氏筛线性筛
定义
为i约数个数
为i约数平方和
为i最小质因子的幂
为i除去最小质因子后剩下的数
显然是积性函数
证明
关于,用乘法分配律证明,过程。。。YY一下吧
大概就是对于n中每个因子,与m的每个因数相乘可以得到mn的一个因数,对平方和的贡献就是新因数的平方,也就是m因数的平方乘以n该因子的平方,将这串式子中的该因子平方提出来就能用b[m]和该因子表示新的平方和的一部分,而将所有n的因子都这样处理,可以YY得到
然后貌似就可以筛了
质数q
当 即
当q|p时,即
解释一下
类似上面对积性的证明,考虑q这个质数(prime[j])对p因数的贡献,是乘上不含q的因数,不含q的因数就有个,现在有个q因子,那么对pq因子个数贡献就乘上
与此类似,q对pq平方和的贡献是p中不含q的因数平方和乘上得出,打快速幂也可,但是这个式子显然可以转化,因为,所以可以得到上式
最后因为是所有数的因数,所有奇数的a[i]需要++,不特判1,因为没说j小于i
#include<cstdio>
#include<cstring>
using namespace std;
const long long mod=1000000007;
const int maxx=10000007;
int ask[3000005];
long long a[maxx],b[maxx],c[maxx],d[maxx],prime[maxx];
bool flag[maxx];
int main(){
int Q;scanf("%d",&Q);
long long q,A,B,C;
scanf("%lld%lld%lld%lld",&q,&A,&B,&C);
for(int ass=1;ass<=Q;++ass){
ask[ass]=q;
q=(q*A+B)%C+1;
}
int cnt=0;
a[1]=1,b[1]=1;
for(int i=2;i<=C;++i){
if(!flag[i]){
prime[++cnt]=i;
a[i]=2;b[i]=(1ll*i*i+1)%mod;c[i]=1;d[i]=1;
}
for(int j=1;j<=cnt&&prime[j]*i<=C;++j){
int pq=i*prime[j];
flag[pq]=1;
if(i%prime[j]==0){
a[pq]=(a[i]/(c[i]+1)*(c[i]+2))%mod;
b[pq]=(b[i]+b[d[i]]*(i/d[i]*prime[j])*(i/d[i]*prime[j]))%mod;
c[pq]=c[i]+1;
d[pq]=d[i];
break;
}
a[pq]=(1ll*a[i]*a[prime[j]])%mod;
b[pq]=(1ll*b[i]*b[prime[j]])%mod;
c[pq]=1;d[pq]=i;
}
}
long long ans1=0,ans2=0;
for(int i=1;i<=Q;++i){
ans1=(ans1+a[ask[i]])%mod;
ans2=(ans2+b[ask[i]])%mod;
if(ask[i]&1){
ans1++;
ans2+=4;
}
}
printf("%lld\n%lld\n",ans1%mod,ans2%mod);
return 0;
}
貌似Day1神奇的函数不用暴力%prime[j],借鉴本题一小部分处理就行。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】