2022牛客寒假算法基础集训营2

比赛链接
H 小沙的数数
题目分析:
优质题解
题眼 a xor b 的最优情况为 a + b,所以原序列最大的xor即为m,数m二进制中有几位1,答案就是这些1分配到n个位置,即为n的x次方,注意刚开始也要取模
在这里插入图片描述
代码:

#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define pf push_front
#define int long long
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f, mod = 1e9 + 7;
const int N = 1e5 + 10;
int n, m;
int qmi(int a, int b, int p){
    int ans = 1;
    while (b){
        if (b & 1) ans = ans * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return ans;
}
int lowbit(int x){
    return x & (-x);
}
signed main(){
    cin >> n >> m;
    n %= mod;
    int cnt = 0;
    while (m){
        m -= lowbit(m);
        cnt ++;
    }
    cout << qmi(n, cnt, mod) << endl;
    return 0;
}

E 小沙的长路
题目分析:
最长路的最大值:
要使得最长路最长,那一定要尽可能多地经过每一条路,因此如果我从一个点出发,一定有一条最长路最后回到这个点(因为完全图每个点度数至少为2,要想全部经过至少我这个出发点的几条要尽量都经过),那问题就变成给一个完全图删边能构成的最长欧拉回路问题,欧拉回路有一个定理:能构成欧拉回路的充要条件是这个图中最多有两个奇数度的点或者都是偶数度的点,所以当n为奇数时本身就存在欧拉回路,当n为偶数时,我们想办法去掉尽可能少的边使这个图只有两个奇数度的点,这个很好想,去掉一条边可以使两个结点的度数变成偶数,最后要剩下两个所以一共要去掉n/2-1条边
最短路见官方题解
图论定义学的很糟
能构成欧拉回路的充要条件是这个图中最多有两个奇数度的点或者都是偶数度的点
在这里插入图片描述
代码:

#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define pf push_front
#define int long long
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f, mod = 1e9 + 7;
const int N = 1e5 + 10;
int n;
signed main(){
    cin >> n;
    if (n & 1) cout << n - 1 << ' ' << n * (n - 1) / 2 << endl;
    else cout << n - 1 << ' ' << n * (n - 1) / 2 - n / 2 + 1 << endl;
    return 0;
}

F 小沙的算数
题目分析:
将乘法的操作数都合并到一起,可以用并查集
如果改变的是加法操作数,直接对答案作加减
如果是乘法操作数,就将其属于的那一块乘法表达式的值从答案中删掉,然后将那块表达式的值除去修改的数乘上新的数,更新之后再加到答案中
思路很明确,但是被取模困住了
因为数字很大,所有每做一次操作都要取一次模。然后一块乘法表达式的值就变得很小,这时候如果想要除去之前其因数的话,除去的因数就可能比这个整个取模之后的表达式的值大。
这个时候就需要一个技巧,逆元(就相当于是倒数)。
通过求这个因数的逆元,将除法转化为乘法
代码:

#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define pf push_front
#define int long long
#define fast ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f, mod = 1e9 + 7;
const int N = 1e6 + 10;
int n, m;
int a[N], p[N], val[N];
char op[N];
bool st[N];
int qmi(int a, int b, int p){
    int ans = 1;
    while (b){
        if (b & 1) ans = ans * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return ans;
}
int find(int x){
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
signed main(){
    fast;
    cin >> n >> m;
    for (int i = 1; i < n; i ++) cin >> op[i];
    for (int i = 1; i <= n; i ++) cin >> a[i], val[i] = a[i];

    int ans = 0;
    for (int i = 0; i <= n; i ++) p[i] = i;

    for (int i = 1; i <= n; i ++){
        if (op[i] == '*'){
            p[i + 1] = p[i]; // 合并乘法连通块
            st[i] = st[i + 1] = true; // 标记为乘法
            val[p[i]] = val[p[i]] * a[i + 1] % mod; // 计算乘法连通块的值
        }
    }
    for (int i = 1; i <= n; i ++) // 预处理出初始答案方便后续计算
        if (p[i] == i) ans = (ans + val[i]) % mod; 
    while (m --){
        int pos, x;
        cin >> pos >> x;
        if (st[pos]){ // 乘法
            int fa = p[pos];
            ans = (ans - val[fa] + mod) % mod;
            //注意逆元之后记得立即取模
            val[fa] = val[fa] * qmi(a[pos], mod - 2, mod) % mod * x % mod;
            ans = (ans + val[fa]) % mod;
            a[pos] = x;
        }
        else { // 加法
            ans = (ans + x - a[pos] + mod) % mod;
            a[pos] = x;
        }
        cout << ans << endl;
    }
    return 0;
}

A 小沙的炉石
题目分析:
在这里插入图片描述
代码:

#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define pf push_front
#define int long long
#define fast ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f, mod = 1e9 + 7;
const int N = 1e6 + 10;
int n, m, q, x;
signed main(){
    cin >> n >> m >> q;
    while (q --){
        cin >> x;
        int l = 0, r = min(n, m + 1);
        while (l < r){
            int mid = l + r + 1 >> 1;
            if (mid * mid <= x) l = mid;
            else r = mid - 1;
        }
        int ans = m * l + l * (l + 1) / 2;
        if (ans >= x) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值