10.5 a.m.小结
作者:互联网
T1:问题 A: 反素数 Antiprime
题目描述
如果一个大于等于 1 的正整数 n,满足所有小于 n 且大于等于 1 的所有正整数的约数个数都小于 n 的约数个数,则 n 是一个反素数。譬如:1, 2, 4, 6, 12, 24,它们都是反素数。
请你计算不大于 n 的最大反素数。
输入
一行一个正整数 n。
输出
只包含一个整数,即不大于 n 的最大反素数。
样例输入
1000
样例输出
840
提示
【数据范围与提示】
对于 10% 的数据,1≤n≤103 ;
对于 40% 的数据,1≤n≤;
对于 100% 的数据,1≤n≤2× 。
题解:
首先可以得知,每一个因子的次数不宜太多,不过2的次数可以较多。显然,可以通过枚举质数的系数来枚举一些数,然后用桶的思想装起来,最后从上往下找到一个次数最高的数,然后作为答案输出即可。所以枚举时宁愿多取数,也不要漏答案。
参考代码:
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
int n,prime[200]={0,2,3,5,7,11,13,17,19,23,29};
LL lg[2000],sum=1ll,lei[200010];
void dfs(int k)
{
LL st=1ll;
for(int i=1;i<=(k==1?31:5);i++)
{
st*=prime[k];
if(st*sum<=n)
{
sum*=st;
lg[k]=i;
dfs(k+1);
lg[k]=0;
sum/=st;
}
else
{
LL pt=1ll;
for(int j=1;j<k;j++)
pt*=(lg[j]+1ll);
if(lei[pt]>sum) lei[pt]=sum;
break;
}
}
}
int main()
{
memset(lei,127/3,sizeof(lei));
scanf("%d",&n);
for(int i=1;i<=30;i++) lg[i]=1ll;
dfs(1);
for(int i=20000;i>=1;i--)
{
if(lei[i]<=n)
{
printf("%d",lei[i]);
break;
}
}
return 0;
}
T2:问题 B: 五指山
题目描述
大圣在佛祖的手掌中。
我们假设佛祖的手掌是一个圆圈,圆圈的长为 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< ,0<d<n,0≤x,y<n。
题解:
这道题可以转化成同余的问题。由于大圣只能逆时针飞行,所以路线已经固定。如果数据不大,完全可以用模拟来做……因此就是将原问题完全变成求一个同余方程的x、y。这一点用exgcd就可以解决。注意n也要缩小gcd倍。
参考代码:
#include<cstdio>
#define LL long long
using namespace std;
LL n,d,x1,y1,t;
LL exgcd(LL a,LL b,LL & x,LL & y)
{
if(b==0)
{
x=1;y=0;return a;
}
LL d=exgcd(b,a%b,x,y);
LL z=x;x=y;y=z-(a/b)*y;
return d;
}
int main()
{
scanf("%lld",&t);
while(t--)
{
scanf("%lld%lld%lld%lld",&n,&d,&x1,&y1);
LL qp=(y1-x1+n)%n;
LL x,y;
LL gcd=exgcd(d,n,x,y);
n=n/gcd;
if((y1-x1)%gcd!=0) printf("Impossible\n");
else
{
if(x*(y1-x1)%gcd!=0) printf("Impossible\n");
else
{
x=((x*(y1-x1)/gcd)%n+n)%n;
printf("%lld\n",x);
}
}
}
return 0;
}
T3:问题 C: Matrix Power Series
题目描述
给定n×n矩阵A和正整数k,求和。
输入
输入只包含一个测试用例。
第一行输入包含三个正整数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≤,
1≤m<
题解:
对于这道题,显然会用到矩阵加速递推。刚开始考虑推导通项公式,结果发现分母会带矩阵。由于不知道这样如何处理,就想办法用其他方法来表示一个普通的等比数列。之前有一道题带给了我思路,即已知一个数(a1、a2、……ak均为质数),那么X的因子个数为:。证明方式就是把括号全部拆开,发现每一个因子都能找到。由此我想到了一种特殊的构造方式,对于一个数k,如果恰好,那么从1到k的等比数列之和可以表示为,注意,最后是2的p-1次方。把括号打开就能得到所有需要求的数。把这个公式延伸到矩阵中,1就是单位矩阵,从左上到右下一条对角线为1,其余全部为0。顾名思义,任意矩阵B与单位矩阵相乘就是本身。-1就是-单位矩阵。此时就可以用O(logn)的效率解决的值,然后求出答案。
现在来考虑k一般化。此时可以采用倍增的思想,先把能求的求了。即:先求出最大能求出的连续等比的和,就用上述的公式以logn的效率解决。此时来看剩下的,把这些单独看成新的一段,也就是说提取公因式,让最小的一个次数为1,自然又变成了一个相同的问题,再次用公式解决能解决的,最后剩下的再提取公因式……提取的公因式如何求?直接用快速幂解决。因为有些特殊的如、、等很可能会重复出现,因此这些值可以预处理出来(预处理就不要用快速幂了,直接由2个上一项加起来就能得到这一项)。注意,为了问题的简便,此处把A的2的n次方处理出来后,要加上一个单位矩阵,就减少了+1的步骤。
可以采用2个while循环实现倍增的过程,最外层为rest,表示还剩下多少项需要处理,每次新一轮循环需要初始化。第二个枚举最多能到2的多少次方,同时每枚举一次,就累乘一次答案,定位ret1。同时还要用p记录枚举到2的第几个次方,直接找记忆化的值,pre表示已经解决的项数(其实可以不用,用rest就行,不过为了思路清晰,还是写上),用于上述讲到的公因式的处理,要用快速幂。讲到快速幂就需要注意:转移矩阵是输入的矩阵,不能+1,这一点要格外注意。而且每次快速幂的时候,单位矩阵和转移矩阵都要初始化。用qpow能处理出ret。最后每一次累加ret*ret1就能够得到ans答案。(代码中的UP数组为中转数组)
参考代码:
#include<cstdio>
#define LL long long
using namespace std;
LL n,m,k,rest,t,pre,A[31][31][35],p;
LL CH[31][31],ret[31][31],TR[31][31],st[31][31];
LL UP[31][31],ret1[31][31],ans[31][31];
void qpow(LL z)
{
for(int i=1;i<=n;i++)
{
ret[i][i]=1ll;
for(int j=1;j<=n;j++)
CH[i][j]=st[i][j];
}
while(z)
{
if(z&1)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int h=1;h<=n;h++)
TR[i][j]=(TR[i][j]+ret[i][h]*CH[h][j])%m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
ret[i][j]=TR[i][j];
TR[i][j]=0;
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int h=1;h<=n;h++)
TR[i][j]=(TR[i][j]+CH[i][h]*CH[h][j])%m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
CH[i][j]=TR[i][j];
TR[i][j]=0;
}
z/=2ll;
}
}
int main()
{
scanf("%lld%lld%lld",&n,&k,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
scanf("%lld",&A[i][j][0]);
st[i][j]=A[i][j][0];
}
t=1ll;p=1ll;
while(t*2ll-1ll<=k)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int h=1;h<=n;h++)
A[i][j][p]=(A[i][j][p]+A[i][h][p-1]*A[h][j][p-1])%m;
p++;t*=2ll;
}
for(int i=0;i<p;i++)
for(int j=1;j<=n;j++)
A[j][j][i]++;
rest=k;
while(rest)
{
for(int i=1;i<=n;i++)
ret1[i][i]=1;
t=1ll;p=1ll;
while(t*2ll-1ll<=rest)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int h=1;h<=n;h++)
UP[i][j]=(UP[i][j]+ret1[i][h]*A[h][j][p-1])%m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
ret1[i][j]=UP[i][j];
UP[i][j]=0;
}
p++;t*=2ll;
}
for(int i=1;i<=n;i++)
ret1[i][i]=(ret1[i][i]-1+m)%m;
qpow(pre);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int h=1;h<=n;h++)
UP[i][j]=(UP[i][j]+ret1[i][h]*ret[h][j])%m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
ans[i][j]=(ans[i][j]+UP[i][j])%m;
UP[i][j]=ret1[i][j]=ret[i][j]=0;
}
pre+=t-1ll;
rest-=t-1ll;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
printf("%lld ",ans[i][j]);
printf("\n");
}
return 0;
}
T4:问题 D: Xiao 9*大战朱最学
题目描述
自从朱最学搞定了QQ农场以后,就开始捉摸去QQ牧场干些事业,不仅在自己的牧场养牛,还到阿九的牧场放牛!
阿九很生气,有一次朱最学想知道阿九牧场奶牛的数量,于是阿九想狠狠耍朱最学一把。
举个例子,假如有16头奶牛,如果建了3个牛棚,剩下1头牛就没有地方安家了。
如果建造了5个牛棚,但是仍然有1头牛没有地方去,然后如果建造了7个牛棚,还有2头没有地方去。
你作为阿九的私人秘书理所当然要将准确的奶牛数报给阿九,你该怎么办?
输入
第一行包含一个整数n表示建立牛棚的次数。
接下来n行,每行两个整数ai,bi, 表示建立了ai个牛棚,有bi头牛没有去处。
你可以假定不同ai之间互质。
输出
输出包含一个正整数,即为阿九至少养奶牛的数目。
样例输入
3 3 1 5 1 7 2
样例输出
16
提示
【数据范围】
1≤n≤10,
1≤ai,bi≤1200000
题解:
这道题就是“曹冲养猪”,一模一样,样例都没变,直接用exgcd解决即可。
参考代码:
#include<cstdio>
#define LL long long
using namespace std;
int n;LL a1,b1,a2,b2;
LL exgcd(LL a,LL b,LL & x,LL & y)
{
if(b==0)
{
x=1;y=0;return a;
}
LL d=exgcd(b,a%b,x,y);
LL z=x;x=y;y=z-(a/b)*y;
return d;
}
int main()
{
scanf("%d",&n);
scanf("%lld%lld",&a1,&b1);
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&a2,&b2);
LL x,y,gcd;
gcd=exgcd(a1,a2,x,y);
x=((x*(b2-b1)/gcd)%a2+a2)%a2;
b1=((a1*x+b1)%(a1*a2)+a1*a2)%(a1*a2);
a1=a1*a2;
}
printf("%lld",b1);
return 0;
}
T5:问题 F: Goldbach's Conjecture
题目描述
哥德巴赫猜想:任何大于 4 的偶数都可以拆成两个奇素数之和。 比如:
你的任务是:验证小于 的数满足哥德巴赫猜想。
输入
多组数据,每组数据一个 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≤ 。
题解:
凭感觉我就知道输出不可能是不可能输出的!(-……-)
首先筛素数,1000就行了。然后题目要求输出差最大的一组,因此直接从最小的奇素数开始搜,然后看n-prime[j]是不是也是一个质数,直接看mf的值是不是等于本身即可。找到一个就直接输出,自然能够满足题目要求。
参考代码:
#include<cstdio>
using namespace std;
int prime[1000100],mf[1000100],cnt=0,n;
int main()
{
for(int i=2;i<=1000000;i++)
{
if(!mf[i])
{
mf[i]=i;
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&prime[j]*i<=1000000;j++)
{
mf[i*prime[j]]=prime[j];
if(mf[i]==mf[i*prime[j]]) break;
}
}
while(1)
{
scanf("%d",&n);
if(n==0) break;
for(int i=2;i<=cnt;i++)
{
if(mf[n-prime[i]]==n-prime[i])
{
printf("%d = %d + %d\n",n,prime[i],n-prime[i]);
break;
}
}
}
return 0;
}
标签:输出,10.5,int,矩阵,LL,样例,小结,31 来源: https://blog.csdn.net/Penguin_Wang/article/details/120617057