考场上没有想出来,看到的数据立马以为是妥妥的结论题,实际上打了表发现并没有发现有什么规律,然后就转而想到一定是有个通项公式吧,所以早上两个小时考试一个半小时最后做了一个十分钟就打得出来的暴力(的我竟然还想着求逆元???)。
点击查看代码
| |
| |
| |
| |
| using namespace std; |
| int n,m; |
| int power(int a,int b,int p) |
| { |
| int ans=1; |
| while(b) |
| { |
| if(b&1) |
| ans=ans*a%p; |
| a=a*a%p; |
| b>>=1; |
| } |
| return ans; |
| } |
| int cnt(int x) |
| { |
| int sum=0; |
| while(x) |
| { |
| sum++; |
| x/=10; |
| } |
| return power(10,sum,m); |
| } |
| bool isp(int x) |
| { |
| if(x==1)return 0; |
| int i; |
| for(i=2;i*i<=x;i++) |
| if(x%i==0)break; |
| if(i*i<=x)return 0; |
| return 1; |
| } |
| signed main() |
| { |
| freopen("math.in","r",stdin); |
| freopen("math.out","w",stdout); |
| scanf("%lld%lld",&n,&m); |
| // if(isp(m)) |
| // { |
| // int inv9=power(9,m-2,m); |
| // long long x1=power(10,n+1,m),x2=(-10+m)%m; |
| // long long tmp1=n/m+1,x3=(tmp1*m-n)%m; |
| // long long ans=((x1+x2)*inv9%m+x3)%m; |
| // long long ans=((power(10,n+1,m)-10+m)%m*inv9%m-n+m)%m; |
| // ans=(ans%m*inv9)%m; |
| // printf("%lld",ans); |
| // } |
| // else |
| // { |
| long long ans=0; |
| for(int i=1;i<=n;i++) |
| ans=((ans*cnt(i))%m+i)%m; |
| printf("%lld",ans); |
| // } |
| return 0; |
| } |
| //int inv[100]; |
| //void get_inv(long long n,long long p) |
| //{ |
| // inv[1]=1; |
| // for(int i=2;i<=n;++i) |
| // inv[i]=p-1ll*(p/i)*inv[p%i]%p,printf("%d\n",inv[i]); |
| //} |
所以除了通项和规律,难道就没有办法通过这么大的数据了吗?肯定是有的!那就是矩阵乘法,只是本蒟蒻一直都和抗拒矩阵乘法(因为写不来!),详情见我第一篇博客为何要使用推通项的方法来做。所以今天一定要把矩阵乘法给拿捏了!
首先,矩阵乘法为什么快呢?因为矩阵乘法可以使用快速幂啊,通过结合律(是没有交换律的)再结合快速幂的思想,我们很容易就能在logN的时间内求出这个矩阵的N次方。
其中乘法的初值应该为单位矩阵(乘任意矩阵都是该矩阵)
首先是存矩阵的二维数组、其次是矩阵的行,列
点击查看代码
| struct Matrix{int a[maxn][maxn],n,m;Matrix{memset(a,0,sizeof a);};} |
其实很简单,按照定义来赋值,然后再赋值行 列就行了
点击查看代码
| Matrix base(int n) |
| { |
| Matrix tmp;tmp.m=tmp.n=n; |
| for(int i=1;i<=n;i++)tmp.a[i][i]=1; |
| return tmp; |
| } |
点击查看代码
| Matrix operator *(Matrix a,Matrix b) |
| { |
| Matrix tmp;tmp.n=a.n,tmp.m=b.m; |
| for(int i=1;i<=a.n;i++) |
| { |
| for(int j=1;j<=b.m;j++) |
| { |
| int c=0; |
| for(int k=1;k<=a.m;k++) |
| c+=a.a[i][k]*b.a[k][j]; |
| tmp.a[i][j]=c; |
| } |
| } |
| return tmp; |
| } |
点击查看代码
| Matrix Mpower(Matrix a,int b) |
| { |
| Matrix ans=base(a.n); |
| while(b) |
| { |
| if(b&1) |
| ans=ans*a |
| a=a*a; |
| b>>=1; |
| } |
| return ans; |
| } |
| |
好了,接下来就是这道题的转移矩阵了,写出转移矩阵和初始矩阵对于我们这种蒟蒻来说也是非常重要的,值得一提的是,我们现在拥有的递推方程是的位数。
那么我们就把这个递推式里面所有的元素写下来(假设我们当前已知右边所有的值):注意到从这个式子的到下个式子的,我们是需要加上一个常数的,所以我们还需要一个常数来辅助,那么当前确定的就是
要转移到
首先根据矩阵的性质和我们要转移的矩阵,我们写出的转移矩阵肯定是的
结合递推公式分析前者的行和后者的列的对应关系,就可以推出每一行的系数是多少
综合起来就是这样的:
这样再做快速幂就差不多了
点击查看代码
| #include<cstdio> |
| #include<cstring> |
| #include<iostream> |
| #include<algorithm> |
| #define int long long |
| using namespace std; |
| const int maxn=15; |
| struct Matrix |
| { |
| int a[12][12];int n,m; |
| Matrix(){memset(a,0,sizeof a);} |
| }t[maxn*maxn]; |
| int N,M; |
| Matrix operator *(Matrix a,Matrix b) |
| { |
| Matrix tmp; |
| for(int i=1;i<=a.n;i++) |
| for(int j=1;j<=b.m;j++) |
| { |
| int c=0; |
| for(int k=1;k<=b.n;k++) |
| c=(c%M+(a.a[i][k]%M)*(b.a[k][j]%M)%M)%M; |
| tmp.a[i][j]=c; |
| } |
| tmp.n=a.n,tmp.m=b.m; |
| return tmp; |
| } |
| Matrix base(int n,int m) |
| { |
| Matrix tmp;tmp.n=n,tmp.m=m; |
| for(int i=1;i<=n;i++)tmp.a[i][i]=1; |
| return tmp; |
| } |
| Matrix Mpower(Matrix a,int b) |
| { |
| Matrix ans=base(3,3); |
| while(b) |
| { |
| if(b&1) |
| ans=ans*a; |
| a=a*a; |
| b>>=1; |
| } |
| return ans; |
| } |
| int power(int a,int b) |
| { |
| int ans=1; |
| while(b) |
| { |
| if(b&1) |
| ans=ans*a; |
| a=a*a; |
| b>>=1; |
| } |
| return ans; |
| } |
| int cnt=0; |
| void form(int tar) |
| { |
| long long ch=1; |
| for(int now=1;now<=tar;now++) |
| { |
| cnt++;ch*=10; |
| t[cnt].a[1][1]=ch,t[cnt].a[1][2]=t[cnt].a[2][2]=t[cnt].a[2][3]=t[cnt].a[3][3]=1; |
| t[cnt].n=t[cnt].m=3; |
| } |
| } |
| int num(int x) |
| { |
| int sum=0; |
| while(x) |
| { |
| sum++; |
| x/=10; |
| } |
| return sum; |
| } |
| long long times[20]; |
| void pre() |
| { |
| times[1]=9; |
| for(int i=2;i<=18;i++)times[i]=times[i-1]*10; |
| } |
| void print(Matrix a) |
| { |
| for(int i=1;i<=a.n;i++) |
| { |
| for(int j=1;j<=a.m;j++) |
| printf("%lld ",a.a[i][j]); |
| printf("\n"); |
| } |
| } |
| signed main() |
| { |
| |
| |
| form(18); |
| scanf("%lld%lld",&N,&M); |
| Matrix ans;ans.a[1][1]=0,ans.a[2][1]=1,ans.a[3][1]=1,ans.n=3,ans.m=1; |
| pre(); |
| for(int i=1;i<num(N);i++) |
| ans=Mpower(t[i],times[i])*ans; |
| int tmp=N-power(10,num(N/10))+1; |
| ans=Mpower(t[num(N)],tmp)*ans; |
| printf("%lld",ans.a[1][1]%M); |
| return 0; |
| } |
反正我没做出来
一道数学题洛谷上面难度评级竟然是紫色,没想到我竟然能切掉,最近真的是人品爆棚了。
给定一个的方格,求其中三个顶点都在格点上的三角形的数量
首先我想到的肯定是生物里面算遗传的时候用的方法,比如求一个后代只患一种病的概率,就用所有的减去不患病的概率和两种病都患的概率。这道题也是一样的,我们用所有的三角形数(不论是否合法)减去三点共线的三角形数,就是我们最后的答案了。总的方案数很明显就是,而横和竖重复的方案数也很简单,就是、那么斜着的如何求呢?这一点我受到了机房同学的一点启发,他们在这道题上面想要求斜率来算,但我觉得斜率必定会带来浮点数,这样的话就会使计算变得复杂,所以怎么解决呢?其实斜率无非就是、的比值,那我们为什么不枚举这两个整数呢?进而想到直接两层枚举横纵线段长度,因为这样就能保证所有斜着的共线情况都能够被计算,就是
最后再用总的情况减去不合法就行了。
点击查看代码
| #include<cstdio> |
| #include<iostream> |
| #include<algorithm> |
| #define int long long |
| using namespace std; |
| int C_3(int x){return x*(x-1)*(x-2)/6;} |
| int gcd(int x,int y){return x%y==0?y:gcd(y,x%y);} |
| int n,m; |
| signed main() |
| { |
| scanf("%lld%lld",&n,&m); |
| |
| long long ans1=C_3((n+1)*(m+1))-(n+1)*C_3(m+1)-(m+1)*C_3(n+1); |
| long long ans2=0; |
| for(int i=1;i<=m;i++) |
| for(int j=1;j<=n;j++) |
| ans2+=(m-i+1)*(n-j+1)*(gcd(i,j)-1); |
| ans2*=2; |
| printf("%lld",ans1-ans2); |
| |
| |
| return 0; |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效