Codeforces Round #641 (Div. 2)

Codeforces Round #641 (Div. 2)

A

题意

定义 f(x) 表示 x 的最小非一因子

给出 k 次操作,每次计算 f(x)+x 的值,并令 x=f(x)+x

求经过 k 次操作后的答案 x

思路

显然如果 x 是偶数,最小因子是2

偶数加偶数不会改变奇偶性,所以最后答案就是 k2

如果 x 是奇数,如果最小因子是一定是奇数

奇数加奇数改变奇偶性,划归到偶数情况

因此只要先暴力把 x 变成偶数,再加上剩余次数乘2即可

#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int N,K;
    cin >> N >> K;
    int ans = N;
    while(K --) {
        int r = 0;
        for(int i = 2;i <= ans / i;i ++) {
            if(ans % i == 0) {
                r = i;
                break;
            }
        }
        if(!r) r = ans;
        ans = ans + r;
        if(ans % 2 == 0) break;
    }

    ans += K * 2 ;
    cout << ans << endl;
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

    int T;cin>>T;
    while(T--)
        solve();

    return 0;
}

B

题意

1N 个数字分配权值 s ,现在要从这个序列中选出满足如下要求的最长序列,输出其长度

定义漂亮序列:

序列中任意的数字 ai 整除 ai+1 ,即 ai|ai+1

且它们的权值 si 构成的序列严格递增

思路

显然最坏长度为1

为了保证 s 的单调性,可以将数字按照它们的权值排序

问题转为,在一个前缀中选择最长序列的问题

我们可以枚举以第 i 个数字为序列结尾的情况

再枚举 i 的所有因子,看这个因子是否在这个序列中存在

如果存在,继续以这个因子为结尾向前枚举

单纯向前搜索显然超时,所以在搜索过程中加入记忆化即可

PS:排序时,如果权值相等,为保证权值的严格递增,需要将数字按照降序排列

PS2:如果不加记忆化,不仅时间复杂度不接受,正确性也是不能保证的,因为搜索时忽略前缀的顺序的,比如3,2,1,4 就很有可能出现 4>2>1 的搜索链,所以在搜索时还应该记录因子具体出现的位置。但因为加上了记忆化,前缀都是被搜索过的合法情况,就不用再在意这些,只要第一次搜索合法之后返回的都是合法的答案。

所以本题更加简便的写法应该是DP,这里就不写了

#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int N;
    cin >> N;
    vector<int> s(N + 1);
    vector<int> ids(N + 1);
    for(int i = 1;i <= N;i ++) {
        cin >> s[i];
        ids[i] = i;
    }
    sort(ids.begin() + 1,ids.end(),[&](int a,int b) {
        if(s[a] != s[b]) return s[a] < s[b];
        return a > b;
    });
    
    vector<bool> st(N + 1);
    vector<int> f(N + 1,-1);

    function<int(int)> dfs = [&](int x) {
        if(f[x] != -1) return f[x];
        if(x == 1) {
            return (int)(st[x]);
        }

        vector<int> d;
        d.push_back(1);
        int ans = 1;
        for(int i = 2;i <= x / i;i ++) {
            if(x % i == 0) {
                d.push_back(i);
                if(i * i != x) {
                    d.push_back(x / i);                
                }
            }
        }
        
        for(int i = 0;i < d.size();i ++) {
            if(st[d[i]]) ans = max(ans,1 + dfs(d[i]));
        }

        return f[x] = ans;
    };

    int ans = 1;
    for(int i = 1;i <= N;i ++) {
        int cur = ids[i];
        st[cur] = 1;
        ans = max(ans,dfs(cur));
    }

    cout << ans << endl;
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

    int T;cin>>T;
    while(T--)
        solve();

    return 0;
}

C

题意

求一个序列所有子串的lcmgcd

思路

最暴力的解法就是枚举坐端点,枚举右端点计算

现在考虑如何优化掉对右端点枚举

设对枚举到的左端点 a ,它之后的后缀都会对答案有贡献

可表示为

gcd1=gcd(lcm(a,b),lcm(a,c),lcm(a,d)lcm(a,an))

可以发现,对任意的 lcm(a,x) ,它们都有公因子 a

gcd 实际上是对数字的每个质因子的幂次取 min 的操作

lcm 则是对每个质因子的幂次取 max 的操作

因为区间 gcd 的可以拆成两个数的 gcd ,所以可直接分析只有三个数的情况

gcd=(lcm(a,b),lcm(a,c))

从质因子的视角下看这个式子

设任意一个质因为 pa 的指数为 x ,b 的指数为 y , c 的指数为 z ,对每一个质因子,上式变为

gcd=min(max(x,y),max(x,z))=max(x,min(y,z))

因此有

gcd1=lcm(a,gcd(b,c,d,))

之后每个 gcdi 再取个 gcd 就是答案

#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int N;
    cin >> N;
    vector<int> a(N + 1);
    for(int i = 1;i <= N;i ++) {
        cin >> a[i];
    }
    vector<int> g(N + 1);
    g[N] = a[N];
    for(int i = N - 1;i > 0;i --) {
        g[i] = __gcd(g[i + 1],a[i]);
    }

    int ans = 0;
    for(int i = 1;i < N;i ++) {
        ans = __gcd(ans,g[i + 1] * a[i] / __gcd(a[i],g[i + 1]));
    }

    cout << ans << endl;
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

    //int T;cin>>T;
    //while(T--)
        solve();

    return 0;
}

知乎上有更加对这个式子更详细的证明 怎么证明GCD(a,LCM(b,c))=LCM(GCD(a,b),GCD(a,c))? - 知乎 (zhihu.com)

D

题意

对一个序列进行区间操作

每次可以把一段区间变成这个区间第 |s|+12 大的数

问最后是否可以全变成目标数字 k

思路

显然如果序列中没有 k 一定不行

按区间长度考虑

如果区间长度为2,如果可以把两个数字变为 k

之后只要不断选择长度为3的区间,总可以达成目标

我们先把这上述可以达成要求的串叫好串

如果长度为2的区间不行,比如 [2,1,4,3,5]k=4 时就是不行的

考虑长度为3的情况

不妨设小于 k 为 0, 等于 k 为 1, 大于 k 为 2

  1. [0,0,0],[1,1,1],[2,2,2] ,很显然不做讨论
  2. [0,0,1] ,会将 1 变为 0,对答案不利,同理 [1,2,2] 也是
  3. [0,1,1] 可以化归到长度为2的情况
  4. [0,0,2] 的情况会让一个大数变小数,此时如果这个大数旁边就是一个 k 就会和这个大数构成好串,对答案来说是不优的
  5. [0,2,2] 的情况可以将小数变成大数,然后不断扩散这个大数,直到遇到第一个 k ,之后又会得到一个好串是可行的
  6. 之后对于 [0,1,2],[1,1,2] 的情况同上分析,核心是通过这样的变换是否可以构成一个好串

对长度大于三的区间,可以发现,总可以用长度为三或者长度为二的序列拼出,本质上和长度为三或二的区间一样。

综上,只要对长度为2和3的区间扫一遍,判断是否有满足条件的情况即可

#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int N,K;
    cin >> N >> K;
    bool ok = 0;
    vector<int> a(N + 1);
    for(int i = 1;i <= N;i ++) {
        cin >> a[i];
        ok |= (K == a[i]);
    }
    if(N == 1) {
        if(K == a[1]) {
            cout << "yes\n";
        }else {
            cout << "no\n";
        }
        return;
    }

    if(!ok) {
        cout << "no\n";
    }else {
        for(int i = 1;i < N;i ++) {
            int x = a[i],y = a[i + 1];
            if(x > y) swap(x,y);
            if(x == K) {
                cout << "yes\n";
                return;
            }
        }

        for(int i = 1;i < N - 1;i ++) {
            vector<int> t;
            for(int j = i;j < 3 + i;j ++) {
                t.push_back(a[j]);
            }
            sort(t.begin(),t.end());
            if(t[1] >= K && t[2] >= K) {
                cout << "yes\n";
                return;
            }
        }
        cout << "no\n";
    }
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

    int T;cin>>T;
    while(T--)
        solve();

    return 0;
}
posted @   Mxrurush  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示