Codeforces Round #641 Div2

Codeforces Round #641 Div2.

官方题解:https://www.luogu.com.cn/blog/Caro23333/codeforces-round-641-zhong-wen-ti-xie

小记:

和队友一起打比赛,探讨题目,exciting!

比赛中出现的问题:

  1. A题 想的太复杂,一看到因数,就想用素数分解。

  2. B题看错题目,剩下一个小时才在wa了好几发下知道看错了。

  3. 还是手生了,迭代器和set都不会用了。

收获:

  1. C题前后缀求gcd
  2. 调和级数估算复杂度 1 1 + 1 2 + 1 3 + . . . + 1 n ≈ l n   n \frac{1}{1}+\frac{1}{2}+\frac{1}{3}+...+\frac{1}{n}\approx ln\,n 11+21+31+...+n1lnn

赛后补题的wa点:

  1. 以为看数据不会超过int,其实在过程中,或者经过计算后,变超出int,要使用long long
  2. 细节还是没有想对,想清楚,比如C题的第<二> wa在第6点,看别人的题解后才明白自己那个细节没有想清楚。

A题

#include "bits/stdc++.h"
#define ll long long
using namespace  std;
int main()
{
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        ll n,k;
        scanf("%lld %lld",&n,&k);
        for(int i=1;i<=k;++i)
        {
            if(n%2==0){n+=k*2;
                break;}
            for(int d=3;d<=n;++d)
                if(n%d==0){
                    n+=d;--k;
                    break;
                }
        }
        printf("%lld\n",n);
    }
}

B题

<一>枚举质因数分解

ps:赛场上想的是把质数求出来,然后再把i 一个个进行质因数分解,而得到质因数之后再一个个组合得到因数,显然是 此举变成负优化,实际上,求质因数可以直接枚举,在 O ( n ) O(\sqrt{n}) On 的时间内就可以完成

#include "cstdio"
#include "bits/stdc++.h"
#define ll long long int
using namespace  std;
const int maxn=1e5+10;
int all[maxn];
int rak[maxn];
int main()
{
//    freopen("in.text","r",stdin);
    int cas,n;
    scanf("%d",&cas);
    while (cas--)
    {
        int ans=0;
        memset(rak,0,sizeof(rak));
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&all[i]);
           for(int d=1;d*d<=i;++d)
           {
               if(i%d!=0)continue;
               if(all[i]>all[d])rak[i]=max(rak[i],rak[d]+1);
               if(all[i]>all[i/d])rak[i]=max(rak[i],rak[i/d]+1);
           }
           ans=max(ans,rak[i]);
        }
        printf("%d\n",ans+1);
    }
    return 0;
}
<二> 倍数转移

时间复杂度为 O ( s   l o g   s ) O(s\,log\,s) O(slogs)
因 为 1 1 + 1 2 + 1 3 + . . . + 1 n ≈ ∫ i = 1 i = n 1 x d x l n x ∣ i = 1 i = n = l n   n 因为 \frac{1}{1}+\frac{1}{2}+\frac{1}{3}+...+\frac{1}{n}\approx \int_{i=1}^{i=n}\frac{1}{x}dx ln x|_{i=1}^{i=n}=ln\,n\\ 11+21+31+...+n1i=1i=nx1dxlnxi=1i=n=lnn

#include "bits/stdc++.h"
using namespace  std;
const int maxn=1e5+10;
int s[maxn];
int rak[maxn];
int main()
{
//    freopen("in.text","r",stdin);
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        int n;
        int ans=0;
        memset(rak,0,sizeof(rak));
        scanf("%d",&n);
        for(int i=1;i<=n;++i)scanf("%d",&s[i]);
        for(int i=1;i<=n;++i) {
            for (int d = i*2; d <= n; d += i)
                if (s[d] > s[i])rak[d] = max(rak[d], rak[i] + 1);
        ans=max(ans,rak[i]);
        }
        printf("%d\n",ans+1);
    }
    return 0;
}

C题

官方题解:

首先 令p为质数,k为任意正数,ans为要求的答案, a i a_i ai如题中意,

如果 p k ∣ a n s p^k|ans pkans,则至少有n-1个 a i a_i ai,满足 p k ∣ a i p^k|a^i pkai

因为 l c m ( a i , a j ) lcm({a_i,a_j}) lcm(ai,aj) 得到的集合 包含任意两个数的最小公倍数

<一> 对每n-1个数求gcd,

得到n个数后,再求这n个数的lcm,就是答案。

那么要如何求n-1个数的gcd?用前后缀维护就可以快速求出,代码见官方题解

🐤注意点:

  1. 那个ans要用long long,否则会wa在第9个点
  2. 不要试 a,b=b%a,a; ,这种写法会error
    #include "bits/stdc++.h"
    #define ll long long int
    using namespace  std;
    const int maxn=1e5+10;
    int a[maxn];
    int gcd(int a,int b)
    {
        while (a)
        {
            int tem=a;
            a=b%a;
            b=tem;
        }
        return b;
    }
    int pre[maxn];
    int suf[maxn];
    int main()
    {
    //    freopen("in.text","r",stdin);
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;++i)scanf("%d",&a[i]);
        pre[1]=a[1];
        suf[n]=a[n];
        for(int i=2;i<=n;++i)pre[i]=gcd(pre[i-1],a[i]);
        for(int i=n-1;i>=1;--i)suf[i]=gcd(suf[i+1],a[i]);
     
     
        ll ans=1;
        for(int now,i=1;i<=n;++i)
        {
            if(i==1)now=suf[i+1];
            else if(i==n)now=pre[i-1];
            else now=gcd(pre[i-1],suf[i+1]);
            ans=now*ans/gcd(now,ans);
        }
        printf("%lld\n",ans);
        return 0;
    }
<二> 枚举n个数 找出质因数的次小k

对每个数进行分解,更新质数的最小k和 次小k。

但是这样不能做优化(即不能中断),时间效率很低,会tle在 第11上。

犯错点:

  1. 细节没有想清楚,所以一直wa在第6

如果n个数都有p,那么k最小的两个数的公倍数中为次小k,

如果n-1个数都有p,那么k最小的和没有p的 这两个数组合成的公倍数就为最小k。

  1. 第二个是wa在第9个点上,答案是求公倍数的公因数,因此可能超出int,要使用long long
  2. 这个是tle在第11个点上,这是这个思路的硬伤,不能做中断优化,只能执行到底,而<三>可以解决这个问题
//该代码会tle
#include "bits/stdc++.h"
#define ll long long
using namespace  std;
const int MAXN=1e5+10;
const int BiG=2e5+10;
int a[MAXN];
int kMin[BiG],kMinSec[BiG],kCnt[BiG];
bool noPrim[BiG];
int prim[MAXN],cntPrim=0;
void MakePrim()
{
    for(int i=2;i<BiG;++i)
        if(noPrim[i]==0){
            prim[++cntPrim]=i;
            for(int d=i+i;d<BiG;d+=i)noPrim[d]=1;
        }
}
void Give(int k,int p)
{
    if(kMin[p]<=k){
        if(kMinSec[p]>k)kMinSec[p]=k;
    }
    if(kMin[p]>=k){
        kMinSec[p]=kMin[p];
        kMin[p]=k;
    }
}
int main()
{
//    freopen("in.text","r",stdin);
    MakePrim();

    memset(kMin,0x3f3f3f3f,sizeof(kMin));
    memset(kMinSec,0x3f3f3f3f,sizeof(kMinSec));
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    for(int i=1;i<=n;++i)
    {
        int cnt=0;
        while (a[i]>=prim[++cnt])
        {
            int p=prim[cnt];
            if(a[i]%p==0)
            {
                int k=0;
                ++kCnt[p];
                while (a[i]%p==0){++k;a[i]/=p;}
                Give(k,p);
            }
        }
    }
    ll ans=1;
    for(int i=1;i<cntPrim;++i)
    {
        int p=prim[i];
        if(kCnt[p]==(n))
        {
            while (kMinSec[p]--){
                ans *= p;
            }
        }
        else if(kCnt[p]==n-1)
            while (kMin[p]--)ans*=p;
    }
    printf("%lld\n",ans);
    return 0;
}
<三> 枚举质数找出质因数的次小k

这个就是官方题解的思路了

我是在<二>的基础上改过来的,会犯的错误也和上一样

#include "bits/stdc++.h"
#define ll long long 
using namespace  std;
const int MAXN=1e5+10;
const int BiG=2e5+10;
int a[MAXN];
int kMin[BiG],kMinSec[BiG],kCnt[BiG];
bool noPrim[BiG];
int prim[MAXN],cntPrim=0;
void MakePrim()
{
    for(int i=2;i<BiG;++i)
        if(noPrim[i]==0){
            prim[++cntPrim]=i;
            for(int d=i+i;d<BiG;d+=i)noPrim[d]=1;
        }
}
void Give(int k,int p)
{
    if(kMin[p]<k){
        if(kMinSec[p]>k)kMinSec[p]=k;
    }
    if(kMin[p]>=k){
        kMinSec[p]=kMin[p];
        kMin[p]=k;
    }
}
int main()
{
//    freopen("in.text","r",stdin);
    MakePrim();

    memset(kMin,0x3f3f3f3f,sizeof(kMin));
    memset(kMinSec,0x3f3f3f3f,sizeof(kMinSec));
    int n;
    ll ans=1;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    sort(a+1,a+n+1);

    for(int i=1;i<=cntPrim;++i)
    {
        if(prim[i]>a[2])break;
        int d,fau=0;
        for(d=1;d<=n;++d)
            if(a[d]%prim[i]==0){
                int k=0,tem=a[d];
                while (tem%prim[i]==0){
                    tem/=prim[i];++k;
                }
                Give(k,i);
            }else {
                ++fau;
                if(fau>=2)break;
            }
        if(fau>=2)continue;
        if(d>=(n+1))
        {
            if(fau==0)while (kMinSec[i]--)ans*=prim[i];
            if(fau==1)while (kMin[i]--)ans*=prim[i];
        }
    }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是Mally呀!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值