牛客多校训练4:B和H题解

Basic Gcd Problem

https://ac.nowcoder.com/acm/contest/5669/B

B Basic Gcd Problem

传送门:https://ac.nowcoder.com/acm/contest/5669/B

题目大意:
就是求那个函数值,题目很清楚。

解题思路:
题我的思路是:最后的答案一定是输出的 的幂(至于怎样判断的,可以带几个数试试,),然后就是确定这个幂的指数,我的方法是先预处理出所有数的最大因子
(1)只要这个最大因子不是 ,指数就要加
(2)接着找这个最大因子的最大因子是不是 ,不是 的话继续上面(1)的操作;如果最大因子是 的话就输出答案: 指的是上述加的 的个数,用正规语言来说就是迭代次数)

这道题需要用线性筛来预处理每个数的最大因子是谁,在线性筛的模板中中稍加一些需要维护的信息就好。还需要用到快速幂,套版子就行,注意的一点就是,会爆,要小心,这个坑当时WA了一次。

#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
const int mod = 1e9+7;
int f[N];//f[i]表示i的最大因子
int prime[N],cnt;
int vis[N];
void init() {
    f[1] = 1;
    for (int i = 2; i < N; i++) {
        if (!vis[i])
        {
            prime[cnt++] = i;
            f[i] = 1;
        }
        for (int j = 0; j < cnt && i * prime[j] < N; j++) {
            f[i * prime[j]] = max(f[i * prime[j]],i);
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
}
int qpow(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1) res=1ll*res*a%mod;
        a=1ll*a*a%mod;
        b >>= 1;
    }
    return res;
}
int main()
{
    int t;
    init();
//    for(int i=1;i<=20;i++)
//    {
//        if(!vis[i])
//        cout<<i<<" ";
//     } 
    scanf("%d",&t);
    while(t--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(x==1)
        {
            cout << 1 << endl;
        }
        else{
            int tmp=f[x];
            int cnt=1;
            while(tmp!=1)
            {
                tmp=f[tmp];
                cnt++;
            }
            printf("%d\n",qpow(y,cnt)%mod);
        }
    }
    return 0;
}

H Harder Gcd Problem

传送门:https://ac.nowcoder.com/acm/contest/5669/H

题目大意:
给定整数 ,用 ~ 中的整数来构造出两个最大的集合 每个整数只能用一次,并且要使两个集合相对应的位置不互质,即题目所说的 .输出最大匹配对数和匹配方案。

解题思路:
看了直播的讲解,讲得很清晰,忍不住赞叹一句,妙啊!太妙了!.接下来,我将我从直播中学到的再来复述一遍。

排除一定不会出现的数

数字 一定不出现在匹配的方案中;

大于 的质数一定不会出现在方案中;

贪心策略

倒叙枚举从 中所有的质数的倍数(至于为什么倒叙,在编写代码的时候就能体现出来,会简便许多,这样可以保证每个质数的 倍是第一次出现)

优先处理质数 倍,暂时跳过 倍,对于 的倍数两两之间可以随意匹配,都是满足条件的,如果 的倍数在 的范围内的且没有匹配过的个数是偶数个的话(包括 倍),那么将这些数全部存入答案即可;如果是奇数个(包括 倍),就将 倍的那个数先存储起来,这里我将其存入 数组中,注意:存入 数组中的p的2倍的数都要先标记为已匹配过的数;避免在处理其他质数的时候发生冲突,比如枚举 倍是 ,在枚举 的倍数时, 倍也是 ,此时在枚举 的这一轮, 这个数字是不需要考虑在内的,这个 要么就在没举 的时候直接计入答案,要么存入质数的 倍的那个数组中

最后处理质数的 倍的这个数组,这个数组中的数字两两之间都是可以匹配的,因为都有 这个因子,所以 一定不会等于 .

最后就是代码的编写了,注意初始化和标记,还有就是细节,初始化需用到线性筛来求质数。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N = 200010;
int prime[N],cnt;
bool vis[N];
void init() {
    for (int i = 2; i < N; i++) {
        if (!vis[i])
            prime[cnt++] = i;
        for (int j = 0; j < cnt && i * prime[j] < N; j++) {
            vis[i * prime[j]] = true;
            if (i % prime[j] == 0) break;
        }
    }
}
bool flag[N];//标记是否还能进行匹配 
int main()
{
    int t;
    vector<PII> ans;//存储互相匹配的答案  
    vector<int> st;//存储多余的质数p的二倍  
    vector<int> tmp;//存储质数p的倍数  
    init();
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n); 
        memset(flag,false,sizeof flag); 
        ans=vector<PII> ();
        st=vector<int> ();

        for(int i=n/2;i>=2;i--)
        {
            if(vis[i]) continue;//合数直接跳过
            int count1=0;
            tmp=vector<int> ();
            for(int j=1;j*i<=n;j++)
            {
                if(!flag[j*i]&&j!=2){
                    count1++;
                    tmp.push_back(j*i);
                } 
            }
            if(count1%2==0)
            {
                for(int j=0;j<tmp.size();j+=2)
                {
                    ans.push_back({tmp[j],tmp[j+1]});
                    flag[tmp[j]]=true;
                    flag[tmp[j+1]]=true;
                }
                st.push_back(2*i);
                flag[2*i]=true;
            }
            else{
                ans.push_back({tmp[0],2*i});
                flag[tmp[0]]=true;flag[2*i]=true;
                for(int j=1;j<tmp.size();j+=2)
                {
                    ans.push_back({tmp[j],tmp[j+1]});
                    flag[tmp[j]]=true;
                    flag[tmp[j+1]]=true;
                }
            }
        } 
        if(st.size()>=2)
        {
            if(st.size()%2==0)
            {
                for(int i=0;i<st.size();i+=2)
                {
                    ans.push_back({st[i],st[i+1]});
                    flag[st[i]]=true;
                    flag[st[i+1]]=true;
                }
            }
            else{
                for(int i=1;i<st.size();i+=2)
                {
                    ans.push_back({st[i],st[i+1]});
                    flag[st[i]]=true;
                    flag[st[i+1]]=true;
                }
            }
        }
        cout<<ans.size()<<endl;
        for(int i=0;i<ans.size();i++)
        {
            cout<<ans[i].first<<" "<<ans[i].second<<endl;
        }
    }    
    return 0;
} 

CSDN 这两题博文,欢迎来访,https://blog.csdn.net/qq_45660232/article/details/107474344

全部评论

相关推荐

10-05 23:02
东北大学 Java
我说句实话啊:那时候看三个月培训班视频,随便做个项目背点八股,都能说3 40w是侮辱价
点赞 评论 收藏
分享
牛客101244697号:这个衣服和发型不去投偶像练习生?
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务