暑期MQFMATH训练营1

P6786 「SWTR-6」GCDs & LCMs

题目链接

洛谷P6786

题意

从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)

题目链接

LightOJ1090

题意

给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

题目链接

LightOJ 1045

题意

求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 扔球

题目链接

51Nod 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」樱花

题目链接

LOJ 10202

题意

\(\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

题目链接

GYM102823 G

题意

给定一个数组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

题目链接

GYM103118 B

题意

提供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

题目链接

CF914B

题意

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;
}



posted @ 2021-07-10 09:55  碳素油墨  阅读(61)  评论(0)    收藏  举报