这波就不打题号了
[POI2001][HAOI2007]反素数
https://www.luogu.com.cn/problem/P1463
https://www.luogu.com.cn/problem/P1463
题解:
开始拿到这道题的时候毫无头绪,然而旁边两人都秒了,这波直接心态炸掉,无奈自己自己没做过TAT,好吧,其实这道题是可以打表的,而且并不是很慢
打表程序:
#include<cstdio>
#include<algorithm>
using namespace std;
int vis[1000100],prime[1000100];
int cnt;
inline long long getsum(long long x)
{
long long now,cn,ret=1;
for(int i=1;i<=13;i++){
now=x;
cn=0;
while(now%prime[i]==0){
cn++;
now/=prime[i];
}
ret*=(cn+1);
}
return ret;
}
int main(void)
{
for(int i=2;i<=1000010;i++){
if(vis[i]==0){
vis[i]=i;
prime[++cnt]=i;
}
for(int j=1;j<=cnt && i*prime[j]<=1000010;j++){
if(vis[i*prime[j]]<prime[j] && vis[i*prime[j]]!=0)break;
vis[i*prime[j]]=prime[j];
}
}
long long maxn=0;
for(long long i=1;i<=2000000000;i++){
long long kk=getsum(i);
if(maxn<kk)printf("%lld,",i);
maxn=max(maxn,kk);
}
}
打表标程:
#include<cstdio>
using namespace std;
int ans[501]={0,1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120,20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,332640,498960,554400,665280,720720,1081080,1441440,2162160,2882880,3603600,4324320,6486480,7207200,8648640,10810800,14414400,17297280,21621600,32432400,36756720,43243200,61261200,73513440,110270160,122522400,147026880,183783600,245044800,294053760,367567200,551350800,698377680,735134400,1102701600,1396755360,2001000000};
int n;
int main(void)
{
scanf("%d",&n);
for(int i=1;i<=500;i++){
if(ans[i]>n){
printf("%d",ans[i-1]);
return 0;
}
}
}
好吧蒙混过关的方法讲了,该说正经点的了
关于正解:
设,则显然
设严格递增,并且
也算在内,则如果
并且
,那么显然这个数不可能是反素数,因为交换
会更好,这样以来,交换前乘积和交换后乘积的因数个数相同,但是前者的积比后者的积大,所以相比之下后者会更加优秀。
所以对于一个反素数,当它的是单调上升的时候,其
必为单调递减。
知道了这个性质之后这道题就要好做多了,咱们直接上搜索。
思路的主旨就是在约数个数相同的情况下尽可能地让当前数小
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n;
int p[20]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,51};
long long maxn=-1,num=-1;
void get(long long m,int f,int t,int pr)
{//f为当前质数的编号 ,当前指数<pr
//t为当前约数的个数
if(t>maxn||(t==maxn&&m<num))
num=m,maxn=t;
int j=0,nt;
long long i=m;
while(j<pr)
{
j++;
if(n/i<p[f])break;
nt=t*(j+1);
i=i*p[f];
if(i<=n) get(i,f+1,nt,j);
}
}
int main()
{
cin>>n;
get(1,1,1,30);
cout<<num;
}
五指山
题目描述
大圣在佛祖的手掌中。
我们假设佛祖的手掌是一个圆圈,圆圈的长为 n,逆时针记为:0,1,2,⋯,n−1,而大圣每次飞的距离为 d。现在大圣所在的位置记为 x,而大圣想去的地方在 y。要你告诉大圣至少要飞多少次才能到达目的地。
输入
有多组测试数据。
第一行是一个正整数 T,表示测试数据的组数;
每组测试数据包括一行,四个非负整数,分别为如来手掌圆圈的长度 n,筋斗所能飞的距离 d,大圣的初始位置 x 和大圣想去的地方 y。
注意孙悟空的筋斗云只沿着逆时针方向翻。
输出
对于每组测试数据,输出一行,给出大圣最少要翻多少个筋斗云才能到达目的地。如果无论翻多少个筋斗云也不能到达,输出 Impossible。
样例输入
2 3 2 0 2 3 2 0 1
样例输出
1 2
提示
【数据范围与提示】
对于全部数据,2<n<10^9 ,0<d<n,0≤x,y<n。
题解:
这道题错的原因是自己对于exgcd 的运用能力相当的差!所以有必要好好复习
我们知道:
裴蜀定理:对于对于给定的正整数a,b,存在x,y,满足
换句话说:方程有解的充要条件为c是gcd(a,b)的整数倍
知道了这个定理之后,我们就可以用拓展欧几里得算法,对于给定的a和b,求出x,y
inline int exgcd(int a,int b,int &x,int &y)
{
if(b==0){x=1,y=0;return a;}
int d=exgcd(b,a%b,x,y);
int z=x;
x=y;
y=z-(a/b)*y;
return d;
}
求出x,y又有什么用呢?我们要求解的是
等价于
和
是不是很像,解出来的x,y再乘上一个
就是解了
#include<cstdio>
using namespace std;
inline long long exgcd(long long a,long long b,long long &xx,long long &yy)
{
if(b==0){xx=1,yy=0;return a;}
long long dd=exgcd(b,a%b,xx,yy);
long long z=xx;
xx=yy;
yy=z-(a/b)*yy;
return dd;
}
int T;
long long n,d,x,y;
long long ans;
inline long long qpow(long long xx,long long yy)
{
long long ret=1;
while(yy){
if(yy&1){
ret=(ret*xx)%n;
}
xx=(xx*xx)%n;
yy>>=1;
}
return ret;
}
int main(void)
{
scanf("%d",&T);
while(T--){
scanf("%lld%lld%lld%lld",&n,&d,&x,&y);
if(x==y){
printf("0\n");
continue;
}
long long p,q;
long long gd=exgcd(d,n,p,q);
if((y-x)%gd!=0){
printf("Impossible\n");
continue;
}else{
long long ans=(y-x)/gd*p;
long long m=n/gd;
ans=(ans%m+m)%m;
printf("%lld\n",ans);
continue;
}
}
}
Matrix Power Series
题目描述
给定n×n矩阵A和正整数k,求和S=A+A2+A3+…+Ak。
输入
输入只包含一个测试用例。
第一行输入包含三个正整数n,k和m。
接下来n行,每行包含n个非负整数(均不超过32,768),用以描绘矩阵A。
输出
按与描述矩阵A相同的方式,输出将S中所有元素对m取模后得到的矩阵。
样例输入
2 2 4 0 1 1 1
样例输出
1 2 2 3
提示
【数据范围】
1≤n≤30,
1≤k≤10^9,
1≤m<104
题解:
这道题是真的很有价值,做了这道题之后对于矩阵的理解有所长进。这道题和一般的矩阵有点区别,一般题目矩阵里面的元素是一个数,而这道题的元素是一个矩阵!是的一拿到感到非常的头大,甚至有点不知所措(于是我考试就直接跳了这题)这道题的关键在于要有写矩形套矩形的勇气,主要的实践方法就是,写两个乘法,分别是单矩形的和矩形中的矩形的,需要注意的是大矩形中的1元素要转化为一个对角线都为1的小矩形,然后就是令人愉悦的快速幂部分。
之前的题解提到,自己在写矩阵的时候大脑的运算速度十分低下,然而今天我找到了解决的方法,重载*符号
struct Matrix{
long long m[120][120];
Matrix operator *(Matrix b){
Matrix c;
memset(c.m,0,sizeof(c.m));
for(int i=0;i<2*n;i++){
for(int j=0;j<2*n;j++){
for(int l=0;l<2*n;l++){
c.m[i][j]=(c.m[i][j]+(m[i][l]*b.m[l][j])%mod)%mod;
}
}
}
return c;
}
};
这样写就极大的简化了思维复杂度,并且使代码不再那么冗长更方便debug(封装yyds)
那么完整代码如下:
#include<cstdio>
#include<cstring>
#include<string>
#include<cstring>
#include<string>
using namespace std;
long long n,k,mod;
struct Matrix{
long long m[120][120];
Matrix operator *(Matrix b){
Matrix c;
memset(c.m,0,sizeof(c.m));
for(int i=0;i<2*n;i++){
for(int j=0;j<2*n;j++){
for(int l=0;l<2*n;l++){
c.m[i][j]=(c.m[i][j]+(m[i][l]*b.m[l][j])%mod)%mod;
}
}
}
return c;
}
};
inline Matrix solve(Matrix a,Matrix b)
{
Matrix c;
memset(c.m,0,sizeof(c.m));
for(int i=0;i<n;i++){
for(int j=0;j<2*n;j++){
for(int l=0;l<2*n;l++){
c.m[i][j]=(c.m[i][j]+(a.m[i][l]*b.m[l][j])%mod)%mod;
}
}
}
return c;
}
inline Matrix qpow(Matrix x, long long y)
{
Matrix ret;
memset(ret.m,0,sizeof(ret.m));
for(int i=0;i<2*n;i++)ret.m[i][i]=1;
while(y){
if(y&1){
ret=ret*x;
}
x=x*x;
y>>=1;
}
return ret;
}
int main(void)
{
Matrix a;
memset(a.m,0,sizeof(a.m));
scanf("%lld%lld%lld",&n,&k,&mod);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
scanf("%lld",&a.m[i][j]);
a.m[i][j]%=mod;
}
}
Matrix b;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++)
b.m[i][j]=a.m[i][j];
}
for(int i=n;i<2*n;i++){
for(int j=0;j<n;j++){
b.m[i][j]=a.m[i-n][j];
}
}
for(int i=n;i<2*n;i++)b.m[i][i]=1;
for(int i=0;i<n;i++)a.m[i][i+n]=1;
b=qpow(b,k-1);
Matrix ans;
ans=a*b;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
printf("%lld ",ans.m[i][j]);
}
printf("\n");
}
}
曹冲养猪
https://www.luogu.com.cn/problem/P1495
https://www.luogu.com.cn/problem/P1495
题解:有三个方法
法1:
中国剩余定理:这里
#include<cstdio>
#define ll long long
ll n,a[16],m[16],Mi[16],mul=1,X;
inline int rd(){
int io=0;char in=getchar();
while(in<'0'||in>'9')in=getchar();
while(in>='0'&&in<='9')io=(io<<3)+(io<<1)+(in^'0'),in=getchar();
return io;
}
void exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){x=1;y=0;return ;}
exgcd(b,a%b,x,y);
int z=x;x=y,y=z-y*(a/b);
}
int main(){
n=rd();
for(int t=1;t<=n;++t){
int M=rd();m[t]=M;
mul*=M;
a[t]=rd();
}
for(int t=1;t<=n;++t){
Mi[t]=mul/m[t];
ll x=0,y=0;
exgcd(Mi[t],m[t],x,y);
X+=a[t]*Mi[t]*(x<0?x+m[t]:x);
}
printf("%lld",X%mul);
return 0;
}
法2:
一种非常巧妙的方法:
· 法3:
这是一种比较一般的方法,当我们面对一个,单个同余方程时就比较好解决,上文有所提及那要是能将这几个同余方程合并成一个就好了
那么拓展中国剩余定理就出来了,他比中国剩余定理强在,它可以处理除数不两两互质的情况。
那么如何将两个同余方程合并呢?
假设已经求出前k-1个方程组成的同余方程组的一个解为x且有
则前k-1个方程的方程组通解为
那么对于加入第k个方程后的方程组
我们就是要求一个正整数t,使得
转化一下上述式子得
对于这个式子我们已经可以通过扩展欧几里得求解t
若该同余式无解,则整个方程组无解
若有,则前k个同余式组成的方程组的一个解解为
#include<cstdio>
#include<cmath>
#define ll long long
using namespace std;
int n;
long long a[10],m[10];
inline ll exgcd(ll c,ll b,ll &x,ll &y)
{
if(b==0){x=1;y=0;return c;}
ll d=exgcd(b,c%b,x,y);
ll z=x;x=y;y=z-(c/b)*y;
return d;
}
inline ll mul(ll x,ll y)
{
ll ret=0;
while(y){
if(y&1)ret=(ret+x);
x=(x+x);
y>>=1;
}
return ret;
}
int main(void)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld%lld",&m[i],&a[i]);
bool flag=0;
ll pr,p,q;
for(int i=2;i<=n;i++)
{
ll d=exgcd(m[i-1],m[i],p,q);
pr=mul(m[i-1],m[i])/d;
ll k=((a[i]-a[i-1]))%pr;
//if(k%d!=0){flag=1;break;}
p=(p*k/d)%pr;
a[i]=((m[i-1]*p+a[i-1]))%pr;
m[i]=pr;
}
if(flag==1){printf("-1\n");return 0;}
else printf("%lld\n",((a[n])%pr+pr)%pr);
return 0;
}
Goldbach's Conjecture
题目描述
哥德巴赫猜想:任何大于 4 的偶数都可以拆成两个奇素数之和。 比如: 
你的任务是:验证小于 106 的数满足哥德巴赫猜想。
输入
多组数据,每组数据一个 n。
读入以 0 结束。
输出
对于每组数据,输出形如 n = a + b,其中 a,b 是奇素数。若有多组满足条件的 a,b,输出 b-a 最大的一组。
若无解,输出 Goldbach's conjecture is wrong.。
样例输入
8 20 42 0
样例输出
8 = 3 + 5 20 = 3 + 17 42 = 5 + 37
提示
【数据范围与提示】
对于全部数据,6≤n≤10^6 。
题解:
线性筛质数+映射,AC
#include<cstdio>
#include<cmath>
using namespace std;
int prime[300000];
int vis[1000020];
int cnt;
bool check[1000020];
int n;
bool flag=0;
int main(void)
{
for(int i=2;i<=1000010;i++){
if(vis[i]==0){
cnt++;
prime[cnt]=i;
vis[i]=i;
}
for(int j=1;j<=cnt && prime[j]*i<=1000010;j++){
vis[prime[j]*i]=prime[j];
if(vis[i]<prime[j])break;
}
}
for(int i=1;i<=cnt;i++)check[prime[i]]=1;
//for(int i=1;i<=25;i++)printf("%d ",prime[i]);
while(1){
scanf("%d",&n);
if(n==0)break;
flag=0;
for(int i=1;i<=cnt && prime[i]<=n/2;i++){
if(check[n-prime[i]]==1){
flag=1;
printf("%d = %d + %d\n",n,prime[i],n-prime[i]);
break;
}
}
if(flag==0)printf("Goldbach's conjecture is wrong.\n");
}
}
这篇博客详细分析了几道算法竞赛题目,包括POI2001和HAOI2007的反素数问题,利用搜索和性质判断反素数。接着讨论了五指山问题,通过裴蜀定理解决距离最短路径。还介绍了Matrix Power Series,使用矩阵快速幂解决矩阵加法序列。最后,探讨了曹冲养猪问题和Goldbach's Conjecture,涉及中国剩余定理和扩展欧几里得算法在解决同余方程组的应用。

1009

被折叠的 条评论
为什么被折叠?



