Codeforces Round #736 (Div. 2) A-D

本文解析了四道算法题目,包括质数模运算规律、敌军进攻路径优化、社交网络生存博弈以及序列模同余子区间寻找等。通过具体代码实现展示了如何运用贪心策略、动态规划等算法解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A题

给出PPP是一个大于5的质数,有Pmod  a=Pmod  b(2≤a<b≤P)P\mod a=P\mod b(2\leq a\lt b\leq P)Pmoda=Pmodb(2a<bP),找到任意一组a,ba,ba,b

  • 显然Pmod  2=Pmod  (P−1)=1P\mod 2=P\mod (P-1)=1Pmod2=Pmod(P1)=1
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int main(){
    int t, n;
    cin >> t;
    while(t--){
        cin >> n;
        cout << 2 << " " << n - 1 << '\n';
    }
    return 0;
}

B题

  • 上面一行是敌人,如果是0只能由正下方的士兵到达,如果是1只能由左下方或者右下方的士兵到达
  • 贪心策略枚举下面的每一个位置,如果是1,看上面,如果正上方是0,此位置士兵向上走最优;如果正上方不是0,先看左上再看右上,因为是从左面递推过来的
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int main(){
    ios::sync_with_stdio(false);
    int t, n;
    string a, b;
    cin >> t;
    while(t--){
        int ans = 0;
        cin >> n;
        cin >> a >> b;
        vector<int> vis(n);
        for(int i=0;i<n;i++){
            if(b[i] == '0') continue;
            if(a[i] == '0'){
                ans++;
                a[i] = '1';
                vis[i] = 1;
            }else{
                if(i == 0){
                    if(!vis[i + 1] && a[i + 1] == '1'){
                        vis[i + 1] = 1;
                        ans++;
                    }
                }else if(i == n - 1){
                    if(!vis[i - 1] && a[i - 1] == '1'){
                        vis[i - 1] = 1;
                        ans++;
                    }
                }else{
                    if(!vis[i - 1] && a[i - 1] == '1'){
                        vis[i - 1] = 1;
                        ans++;
                    }else if(!vis[i + 1] && a[i + 1] == '1'){
                        vis[i + 1] = 1;
                        ans++; 
                    }
                }
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

C题

给出了一张图上的朋友关系,每个人的标号就是他的权重,如果一个人的所有朋友的权重都比他大,那么这个人在这一轮中会死亡,中间还可以进行加边和删边的操作,每次操作333询问当前状态下最后一轮还能剩多少人,每次操作333都是独立的

  • 第一直觉和点的度数有关系。首先考虑一下,什么样的人会被杀呢?只要他有一个朋友的点权比他大,那么他就会被杀,也就是在连边之前如果点权小的度数为0,他就会被杀。考虑使用一个数组degreedegreedegree记录每条边具有较小点权的点的度数,初始存活人数为nnn,加边的时候,如果度数为0,那么n−1n-1n1,度数加111,防止重复计算;删边的时候,较小点权的点的度数减111,如果减完发现度数为000,说明这个点变成了孤立点,存活下来,n+1n+1n+1
  • 虽然每次操作333之间是独立的,但是边的改变是遗留下来的,所以答案保留
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int degree[MAXN];
int main(){
    ios::sync_with_stdio(false);
    int n, m, u, v, q, op;
    cin >> n >> m;
    int ans = n;
    for(int i=0;i<m;i++){
        cin >> u >> v;
        if(u > v) swap(u, v);
        if(degree[u] == 0){
            ans -= 1;
        }
        degree[u] += 1;
    }
    cin >> q;
    while(q--){
        cin >> op;
        if(op == 1){
            cin >> u >> v;
            if(u > v) swap(u, v);
            if(degree[u] == 0) ans -= 1;
            degree[u] += 1;
        }else if(op == 2){
            cin >> u >> v;
            if(u > v) swap(u, v);
            degree[u] -= 1;
            if(degree[u] == 0) ans += 1;
        }else{
            cout << ans << '\n';
        }
    }
    return 0;
}

D题

给出一个序列,让我们找某个子区间每一个数满足模mmm同余(m≥2)(m\geq2)(m2),问这个子区间最长是多少

  • 如果aimod  k=ajmod  ka_i\mod k=a_j\mod kaimodk=ajmodk,那么有(ai−aj)=k×l(a_i-a_j)=k\times l(aiaj)=k×l,且让找一个子区间,那么不难想到需要计算原序列的差分数组,同时此数组所有数都取绝对值便于判断GCDGCDGCD
  • 那么可以枚举区间的左右端点,如果差分数组的某个区间内所有数的GCD≥2GCD\geq2GCD2,那么这个区间的长度可能就是答案,枚举所有这样的区间更新答案即可
  • 所以需要高效维护数组的区间GCDGCDGCD,属于RMQRMQRMQ问题,可以考虑使用线段树或者STSTST表,这里就使用STSTST表了,使用一圈forforfor枚举区间右端点,再使用一个指针记录左端点,如果发现这个区间的GCD=1GCD=1GCD=1,那么左端点不停的右移直到到达右端点右侧(如果只是到达右端点在更新的时候会出现错误)或者发现GCD≠1GCD\neq 1GCD=1,继续移动右端点,重复操作更新答案
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 2e5 + 100;
const double eps = 1e-6;
ll ST[MAXN][50];
ll a[MAXN], b[MAXN];
int main(){
    ios::sync_with_stdio(false);
    int t, n;
    cin >> t;
    while(t--){
        cin >> n;
        for(int i=1;i<=n;i++) cin >> a[i];
        for(int i=1;i<n;i++){
            b[i] = a[i + 1] - a[i];
            ST[i][0] = abs(b[i]);
        }
        int sz = 31 - __builtin_clz(n);
        for(int j=1;j<=sz;j++){
            for(int i=1;i+(1 << (j - 1))<n;i++){
                ST[i][j] = __gcd(ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);
            }
        }
        int j = 1;
        int ans = 1;
        for(int i=1;i<n;i++){
            int len = i - j + 1;
            sz = 31 - __builtin_clz(len);
            while(j <= i && __gcd(ST[j][sz], ST[i - (1 << sz) + 1][sz]) == 1){
                j++;
                len = i - j + 1;
                sz = 31 - __builtin_clz(len);
            }
            ans = max(ans, i - j + 2);
        }
        cout << ans << '\n';
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Clarence Liu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值