暑期MQFMATH训练营1
P6786 「SWTR-6」GCDs & LCMs
题目链接
题意
从n个数种选取一些数字a1~aj,使得其对于任意数字i要么ai=max(a1.....aj),或者存在ak>ai且ak+ai+gcd(ak,ai)=lcm(ak,ai);
题解
凑了几个样例发现只有当ai/aj=2/3时,才能满足ak+ai+gcd(ak,ai)=lcm(ak,ai);
所以最大的情况分成两种:
1.最大值之和
2.各个链总和的最大值
所以对这两种情况进行遍历就能暴力出结果
AC代码
#include "bits/stdc++.h"
using namespace std;
#define int long long
const int maxn=3e5+10;
inline int rd()
{
int a;scanf("%lld",&a);return a;
}
int n;
int a[maxn];
unordered_map<int,int>mp;
signed main(){
n=rd();
for(int i=1;i<=n;i++){
a[i]=rd();
mp[a[i]]+=a[i];
}
sort(a+1,a+1+n);
int maxx=-1;
for(auto it = mp.begin(); it!=mp.end() ;it++){
maxx=max(maxx,it->second);
}
int res=-1;
int sum;int first=0;
for(int i=1;i<=n;i++) {
sum=0;
if(a[i]%2==0){
int tmp=a[i];
while(mp[tmp/2*3]&&tmp%2==0){
sum+=mp[tmp];
tmp=tmp/2*3;
}
sum+=mp[tmp];
}
res=max(sum,res);
}
printf("%lld\n",max(maxx,res));
return 0;
}
Trailing Zeroes (II)
题目链接
题意
给n,c,p,q
求结果C(n,c)×p^q的后缀0的个数
题解
每一个10都由质因数2和5组成,所以计算公式中的2和5的个数就可以了
求C(n,c)用暴力一个前缀和取出2和5的数量进行计算,分子上面的用前缀和相减算一下,然后再减去分母的个数
p^q同理(不能直接取10的个数,要取2和5也放到公式里计算,wa半天就是这里没处理好)
AC代码
#include "bits/stdc++.h"
using namespace std;
#define int long long
const int maxn=1e6+10;
inline int rd()
{
int a;scanf("%lld",&a);return a;
}
int n,t,r,q,p,m;
int cnt2,cnt5;
int num2[maxn],num5[maxn],sum2[maxn],sum5[maxn];
int weiling(int x){
int sum=0;
while(x%10==0){
x/=10;
sum++;
}
return sum;
}
void init(){
for(int i=1;i<=1000000;i++){
if(i%2==0)num2[i]=num2[i/2]+1;
if(i%5==0)num5[i]=num5[i/5]+1;
}
for(int i=1;i<=1000000;i++) {
sum2[i] = sum2[i - 1] + num2[i];
sum5[i] = sum5[i - 1] + num5[i];
}
};
signed main(){
init();
t=rd();
int cnt=1;
while(t--){
n=rd();r=rd();p=rd();q=rd();
cnt2=0;cnt5=0;
cnt2=sum2[n]-sum2[n-r]-(sum2[r]);
cnt5=sum5[n]-sum5[n-r]-(sum5[r]);
cnt2+=num2[p]*q;
cnt5+=num5[p]*q;
int len=min(cnt2,cnt5);
printf("Case %lld: %lld\n",cnt++,len);
}
return 0;
}
Digits of Factorial
题目链接
题意
求n!在k进制下有几位数
题解
n 的在十进制下的位数 = log10(n)+ 1 => n 在 base 进制下的位数 = log base(n)
换底公式 log base(n) == log(n)/ log(base)
log base (n!) == ( log(n) + log(n-1)+ .... + log(1)) / log(base)
所以只要预处理一下log10的值就可以直接求取位数了
AC代码
#include "bits/stdc++.h"
#define lowbit(x) x&-x
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
inline int rd()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
double a[maxn];
void init(){
a[0]=log10(1);
for(int i=1;i<=1000000;i++)a[i]=a[i-1]+log10(i);
}
int t;
int n,m;
int main() {
init();
t=rd();
int ca=1;
while(t--){
n=rd();m=rd();
printf("Case %d: ",ca++);
double res= ceil(a[n]/log10(m*1.0));
if(n==0)printf("1\n");
else printf("%d\n",(int)res);
}
}
//log di(n) == log(n)/ log(di)
1262 扔球
题目链接
题意
给一个圆形,问在碰撞n次反弹的情况下能有多少种情况回到原点。
题解
对于每一种情况,可以转化为从1出发,每次都移动固定长度的距离,经过每一个点后返回1的问题
因为碰撞n次后到达,所以总共的点为n+1个,即把圆等分为n+1份
即一个1 2 3 ...... n+1的圈一样的数组
枚举几个样例后容易得出只有当步长k和总数n+1互素的时候才能走完全部且不会重复
所以题目就能转化成求n+1的欧拉函数值
AC代码
#include "bits/stdc++.h"
using namespace std;
#define int long long
const int maxn=3e5+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
inline int rd()
{
int a;scanf("%lld",&a);return a;
}
int cnt=1;
int oular(int n){
int res=n;
for(int i=2;i*i<=n;i++){
if(n%i==0)res=res/i*(i-1);
while(n%i==0)n/=i;
}
if(n!=1)res=res/n*(n-1);
return res;
}
signed main() {
int n;
n=rd();
if(n==1){
printf("1\n");
}
else printf("%lld\n",oular(n+1));
return 0;
}
//1 1
//2 2
//3 2
//4 4
//5 2
//6 6
//7 4
//8 6
//9 4
#10202. 「一本通 6.2 练习 5」樱花
题目链接
题意
\(\frac{1}{x}+\frac{1}{y}=\frac{1}{n!}\)
求这个公式的x和y 的正整数解有几个
题解
\(\frac{1}{x}+\frac{1}{y}=\frac{1}{n!}=>\frac{x+y}{xy}=\frac{1}{n!}=>(x+y)n!=xy=>xy-(x+y)n!=0\)
左右两边加上\((n!)^2\)
原式:\((n!)^2-(x+y)n!+xy=0\)
所以解 \(a=n!-x\) 解\(b=n!-y\)
原式:\(ab=(n!)^2\)
只要确定a或者b,就能确定另一个解,因为\(n!\)是一个定值,所以x和y也能确定
因为对于任意一个数字\(m=q_1^{c_1}*q_2^{c_2}*q_3^{c_3}*.....*q_n^{c_n}(q_i为素数)\)
设\(m=(n!)\),那么\(m^2=q_1^{2c_1}*q_2^{2c_2}*q_3^{2c_3}*.....*q_n^{2c_n}(q_i为素数)\)
所以对于其约束解a来说,a的个数和质因数个数的关系是\(n_a=(2*c_1+1)*(2*c_2+1)*......*(2*c_n+1)\)
综上所述,解题方法为用素数筛筛出素数,然后计算\(n!\)的质因数个数进行计算就可以得到答案了
AC代码
#include "bits/stdc++.h"
using namespace std;
#define int long long
const int maxn=1e6+10;
const int mod=1e9+7;
inline int rd()
{
int a;scanf("%lld",&a);return a;
}
int n;
int vis[maxn],prime[maxn],s=0,c[maxn];
void getprime(){
for(int i=2;i<=n;i++){
if(!vis[i]){
vis[i]=i;
prime[++s]=i;
}
for(int j=1;j<=s;j++){
if(vis[i]<prime[j]||prime[j]*i>n)break;
vis[prime[j]*i]=prime[j];
}
}
}
void getc(){
for(int i=1;i<=s;i++){
int now= prime[i];
for(int j=now;j<=n;j*=now){
c[i]+=(n/j);
}
}
}
signed main(){
n=rd();
getprime();
getc();
int res=1;
for(int i=1;i<=s;i++){
res*=(c[i]*2|1);
res%=mod;
}
printf("%lld\n",res);
return 0;
}
//36 1 2 3 4 6 9 12 18 36
G. Greatest Common Divisor
题目链接
题意
给定一个数组a,每次操作可以把所有数字+1,问至少几次可以让数组的gcd不为1,不行则输出-1.
题解
\(gcd(a,b)=>gcd(a,b-a)\)
\(gcd(a,b,c)=>gcd(a,b-a,c-b)\)
\(gcd(a,b,....,z)=>gcd(a,b-a,c-b,......,z-y)\)
由上述公式可以看出当所有数字都进行加一操作时,除了a以外的其他因子的大小其实是不变的,所以可以直接差分求出除了a以外的其他因子进行gcd操作得到sum,然后\(gcd(a,sum)\)就是整个数组gcd的结果了
所以枚举sum的所有质因子,就能找出大于a且和sum的gcd不为一的最小数了,减一下就是结果
AC代码
#include "bits/stdc++.h"
using namespace std;
#define int long long
const int maxn=1e5+10;
const int MAXN=1e7+100;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
inline int rd()
{
int a;scanf("%lld",&a);return a;
}
int n,t,res;
int a[maxn],ch[maxn];
int prime[MAXN],s=0;
void getprime(int n){
for(int i=2;i*i<=n;i++){
if(n%i==0){
prime[++s]=i;
while(n%i==0)n/=i;
}
}
if(n!=1)prime[++s]=n;
}
int gcd(int a,int b){
return !b? a:gcd(b,a%b);
}
int slove(int a,int b){
if(b==0){
if(a==1)return 1;
else return 0;
}
if(gcd(a,b)!=1){
res=0;
return res;
}
getprime(b);
int cnt=inf;
for(int i=1;i<=s;i++){
cnt=min((a+prime[i]-1)/prime[i]*prime[i]-a,cnt);
}
return cnt;
}
signed main(){
t=rd();int cnt=1;
while(t--){
s=0;
res=0;
n=rd();
for(int i=1;i<=n;i++)a[i]=rd();
if(n==1){
if(a[1]==1)res=1;
else res=0;
}
else{
for(int i=1;i<=n;i++){
ch[i]=abs(a[i]-a[i-1]);
}
int gcdd=ch[2];
for(int i=3;i<=n;i++){
gcdd=gcd(gcdd,ch[i]);
}
if(gcdd==1){
res=-1;
}else{
res=slove(ch[1],gcdd);
}
}
printf("Case %lld: %lld\n",cnt++,res);
}
return 0;
}
Build Roads
题目链接
题意
提供n,L,R,seed,每个城市的价值为使用题目中提供的函数生成随机数,两个城市之间的消耗为\(gcd(a,b)\),问链接所有城市后的最小消耗
题解
题目不难,主要是理解题目
1.当n的值够大时随机数中必有质数,这个时候就直接输出n-1
2.如果L==R,那直接输出 \(L*(n-1)\)
3.当数据量够小的时候直接暴力求取
AC代码
#include "bits/stdc++.h"
using namespace std;
#define int long long
const int maxn=2e5+10;
const int MAXN=1e7+100;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
inline int rd()
{
int a;scanf("%lld",&a);return a;
}
int gcd(int a,int b){
return !b?a:gcd(b,a%b);
}
int n,L,R,a[maxn];
unsigned long long seed;
unsigned long long xorshift64(){
unsigned long long x = seed;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
return seed = x;
}
int gen(){
return xorshift64()%(R-L+1)+L;
}
int slove(){
if(L==R)return L*(n-1);//第二种情况
else if(n>10000)return n-1;//第一种情况
else {
int ans=inf;
for(int t=1;t<=100;t++){//主要数据很弱可以直接上
int res=0;
random_shuffle(a+1,a+1+n);//随机调整数的位置
for(int i=2;i<=n;i++){
int gcdd=gcd(a[i],a[i-1]);
for(int j=i-2;j>=1;j--){
gcdd=min(gcdd,gcd(a[i],a[j]));
if(gcdd==1)break;
}
res+=gcdd;
}
if(res==n-1)return n-1;
ans=min(ans,res);
}
return ans;
}
}
signed main(){
srand(time(0));
scanf("%d%d%d%llu",&n,&L,&R,&seed);
for(int i=1;i<=n;i++){
a[i]=gen();
}
printf("%lld\n",slove());
return 0;
}
B. Conan and Agasa play a Card Game
题目链接
题意
Conan and Agasa在玩一个游戏,每次可以取走n和所有比n小的数,最后谁没得拿谁输
题解
当最大值n的个数为偶数时Conan肯定不拿n,拿比n小一点的次最大值,如果次最大值也是偶数的个数,那就取更小的最大值,直到最后,要是有奇数的话,Conan就能取走它和比它小的所有数,实现反杀,把必输局面转移给Agasa
AC代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int,int>
typedef unsigned long long ULL;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
map<int,int>mp;
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++){
int a;
cin>>a;
mp[a]++;
}
int res=0;
for(auto it=mp.rbegin();it!=mp.rend();it++){
if(it->second%2==1)res=1;
}
if(res)printf("Conan\n");
else cout<<"Agasa"<<endl;
return 0;
}

浙公网安备 33010602011771号