题解 | #练习赛129题解#

数数

https://ac.nowcoder.com/acm/contest/90074/A

前言

F不会,剩下的感觉写的也挺丑陋,如果有问题欢迎大家指出


题解


A.数数

埃氏筛的写法,可以枚举所有质数,给因子中有该质数的数字标记+1,

#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;

void solve() {
    int n;
    std::cin >> n;

    std::vector<int> cnt(n + 1);
    for (int i = 2; i <= n; i++) {
        if (!cnt[i]) {
            for (int j = i; j <= n; j += i) {
                cnt[j] += 1;
            }
        }
    }

    int ans = 0;
    for (int i = 2; i <= n; i++) {
        if (cnt[i] > 1) {
            ans++;
        }
    }

    std::cout << ans << '\n';
}  

signed main() {
    std::ios::sync_with_stdio(0);
    std::cout.tie(0);
    std::cin.tie(0);

    i64 t = 1; 
    // std::cin >> t;
    while (t--) {
        solve();
    }
}

B.三位出题人

本题可能最大的阻力是读题吧,就是某一道题不可以被所有人一起看,也不允许某一道题没有人看,那对于每一道题而言,可以被做,那就是,这个数就是,由于有道题,再对这个数求个

#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;

const int mod = 1e9 + 7;

i64 ksm (i64 a, i64 b) {
    i64 res = 1;
    while(b) {
        if (b & 1) {
            res = res * a % mod;
        }
        a = a * a % mod;
        b >>= 1;
    }

    return res;
}

void solve() {
    i64 n, m;
    std::cin >> n >> m;
    std::cout << ksm((ksm(2, m) - 2 + mod) % mod, n) << '\n';
}  

signed main() {
    std::ios::sync_with_stdio(0);
    std::cout.tie(0);
    std::cin.tie(0);

    i64 t = 1; 
    std::cin >> t;
    while (t--) {
        solve();
    }
}

C.和天下

就是一个拆位来做的东西啊,可能是实现起来的时候把大家卡柱了。先考虑一下版本当的时候,是不是只要是任意两个按位与起来大于就可以建边,这个东西实现的时候就是枚举每一位嘛,如果某些数的该二进制位上的数是就把他们放到同一个集合里面他们之间可以两两建边,给大家写一段伪代码参考一下

std::vector<std::vector<int> > a(35);
    for (int j = 0; j <= 32; j++) {
        for (int i = 1; i <= n; i++) {
            if ((a[i] >> j) & 1) {
                v[j].push_back(a[i]);
            }
        }
    }

    for (int i = 0; i <= 32; i++) {
        for (int j = 1; j < v[i].size(); j++) {
            merge(v[i][j], v[i][j - 1]);
        }
    }

再来看本题不就是把变了嘛,但思路不变啊,如果的二进制串是,最高位这位,那假设有这两个数,他们的最高位这位,这两个数按位与运算之后的数是不是一定大于了,那你可能会问了,那最高位的最高位在同一个位置的怎么办,那我们就继续去看下一位嘛,比如的二进制串是,你有两个数是,发现了什么,这两个数虽然最高位的相同,但次高位比的大,这个时候我们其实可以就不管位上的那个,把这些数的下一个的位置看做是最高位的位置,在实现的时候可以把这三个数直接减去,这样就可以做到消除最高位相同的影响了啊,剩下的不就回到了hard版本开始的时候,看看那些数的二进制位置上有是比中的“最高位”的位置还高,实现起来可以参考我的代码

#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;

void solve() {
    i64 n, k;
    std::cin >> n >> k;
    
    std::vector<i64> a(n + 1);
    std::vector<int> p(n + 1), cnt(n + 1);
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];

        p[i] = i;
        cnt[i] = 1;
    }

    auto find = [&] (auto self, int x) ->int {
        if (x == p[x]) {
            return x;
        }
        else {
            return p[x] = self(self, p[x]);
        }
    };

    auto merge = [&] (int u, int v) -> void {
        int fx = find(find, u), fy = find(find, v);
        if (fx != fy) {
            p[fx] = fy;
            cnt[fy] += cnt[fx];
        }
    };

    if (k == 0) {
        std::cout << n << '\n';
        return;
    }

    int target = 0;
    for (int i = 63; i >= 0; i--) {
        if ((k >> i) & 1) {
            target = i;
            break;
        }
    }

    // std::cout << target << '\n';
	//这里是在k不改变的情况下,有那些数进行按位与操作之后的大小比k大
    std::map<int, std::vector<int> > mp;
    for (int i = 1; i <= n; i++) {
        for (int j = 63; j > target; j--) {
            if ((a[i] >> j) & 1) {
                mp[j].push_back(i);
            }
        }
    }
	//这里是有哪些数的最高位1,小于等于k的最高位1的时候
    std::vector<int> vis(n + 1, 1);
    for (int i = 1; i <= n; i++) {
        for (int j = target; j >= 0; j--) {
            if (((k >> j) & 1) == 0) {
                if ((a[i] >> j) & 1 && vis[i]) {
                    mp[j].push_back(i);
                }
            }

            if (vis[i] && ((k >> j) & 1) && (((a[i] >> j) & 1) == 0)) {
                vis[i] = 0;
            }
        }
    }

    std::vector<int> v;
    for (int i = 1; i <= n; i++) {
        if (vis[i]) {
            v.push_back(i);
        }
    }


    for (int i = 1; i < v.size(); i++) {
        merge(v[i], v[i - 1]);
    }
    
    for (auto [it, v] : mp) {
        for (int i = 1; i < v.size(); i++) {
            merge(v[i], v[i - 1]);
        }
    }

    int ans = -1;
    for (int i = 1; i <= n; i++) {
        ans = std::max(ans, cnt[i]);
    }

    std::cout << ans << '\n';
}  

signed main() {
    std::ios::sync_with_stdio(0);
    std::cout.tie(0);   
    std::cin.tie(0);

    i64 t = 1; 
    std::cin >> t;
    while (t--) {
        solve();
    }
}

D.搬家

这个算是很板的倍增题吧,首先用双指针实现从任意一个位置开始装满一个箱子会到哪个位置,然后就是倍增的板子,最后算答案的时候就枚举看看从哪个位置开始装的东西最多,且不会超过,这个不知道还能说点什么了,大家不会的可以先去学一下倍增的思想。代码仅供参考

#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;

void solve() {
    i64 n, m, k;
    std::cin >> n >> m >> k;

    std::vector<i64> a(n + 1);
    for (i64 i = 1; i <= n; i++) {
        std::cin >> a[i];
    }

    i64 sum = 0;
    i64 logn = 31 - __builtin_clz(n);
    std::vector<std::vector<i64> > nxt(logn + 5, std::vector<i64> (n + 2));
    i64 l = 1, r = 1;
    while (l <= r && l <= n) {
        if (r == n + 1) {
            nxt[0][l] = r;
            sum -= a[l];
            l++;
            continue;
        }
        sum += a[r];
        while(sum > k) {
            nxt[0][l] = r;
            sum -= a[l];
            l++;
        }
        r++;
    }

    nxt[0][n + 1] = n + 1;

    for (i64 i = 1; i <= logn; ++i) {
        for (i64 j = 1; j <= n + 1; ++j) {
            nxt[i][j] = nxt[i - 1][nxt[i - 1][j]];
        }
    }

    i64 ans = -1;
    for (i64 i = 1; i <= n; i++) {
        i64 num = m, tmp = 0, l = i;
        for (i64 j = logn; j >= 0; j--) {
            if (num >= (1 << j)) {
                num -= (1 << j);
                tmp += nxt[j][l] - l;
                l = nxt[j][l];
                if (l == n + 1) break;
            }
        }
        ans = std::max(ans, tmp);
    }

    std::cout << ans << '\n';
}  

signed main() {
    std::ios::sync_with_stdio(0);
    std::cout.tie(0);
    std::cin.tie(0);

    i64 t = 1; 
    std::cin >> t;
    while (t--) {
        solve();
    }
}
}

E.Alice and Bod

精彩的线段树来了,我这里开了两棵线段树维护正反哈希,且在每一棵树中分别开了个懒标记,线段树中维护的数组就是每个字母对应的哈希值,比如某一个字符串代表的哈希值就是 + +与常规哈希不同,是把每个字母对应的位置进行哈希。然后就是维护这些东西了,在修改的时候比如是把这一段中的修改成了,那我们可以先记录一下的哈希值,然后把的哈希值赋给类似操作的字母就可以实现区间修改,我说不太明白再具体的维护方法,大家有问题可以来私信或评论。

#include<bits/stdc++.h>
#define lson (rt << 1)
#define rson (rt << 1 | 1)

using i64 = long long;
using u64 = unsigned long long;

const int mod=1610612741;
const int base=13331;

i64 ha[100005][2][26], p[100005], tmp[26];
int n, m;
std::string s;

struct treenode {
    int lz = 0;
    int len = 0;
    i64 vis[26];
    treenode (){
        for (int i = 0; i < 26; i++) {
            vis[i] = 0;
        }
    }
};

struct Segment_Tree {
    std::vector<treenode> tr;

    void init (int n, int i) {
        tr.resize((n + 1) << 2);
        build(1, 1, n, i);
    }

    treenode merge (int rt, treenode a, treenode b) {
        treenode res;
        res.lz = tr[rt].lz;
        res.len = a.len + b.len;
        for (int i = 0; i < 26; i++) {
            res.vis[i] = a.vis[i] * p[b.len] % mod + b.vis[i];
            res.vis[i] %= mod;
        }

        return res;
    }

    void change(int rt, int x) {
        tr[rt].lz += x;
        tr[rt].lz %= 26;
        for (int j = 0; j < 26; j++) {
            tmp[j] = tr[rt].vis[j];
        }
        for (int j = 0; j < 26; j++) {
            int t = (j + x) % 26;
            tr[rt].vis[t] = tmp[j];
        }
    }

    void push_down(int rt) {
        if (tr[rt].lz) {
            change(rt << 1, tr[rt].lz);
            change(rt << 1 | 1, tr[rt].lz);
            tr[rt].lz = 0;
        }
    }

    void build(int rt, int l, int r, int i) {
        tr[rt].lz = 0;
        if (l == r) {
            if (i == 0) {
                tr[rt].vis[s[l] - 'a'] = 1;
            }
            else {
                tr[rt].vis[s[n - l + 1] - 'a'] = 1;
            }
            tr[rt].len = 1;
            return;
        }

        int mid = l + r >> 1;
        build(lson, l, mid, i);
        build(rson, mid + 1, r, i);
        tr[rt] = merge(rt, tr[rt << 1], tr[rt << 1 | 1]);
    }

    void update(int rt, int l, int r, int L, int R, int x) {
        if (l >= L && r <= R) {
            change(rt, x);
            return;
        }
        push_down(rt);
        int mid = l + r >> 1;
        if (mid >= L)
            update(lson, l, mid, L, R, x);
        if (mid < R) {
            update(rson, mid + 1, r, L, R, x);
        }
        tr[rt] = merge(rt, tr[rt << 1], tr[rt << 1 | 1]);
    }

    treenode query(int rt, int l, int r, int L, int R) {
        if (l >= L && r <= R) {
            return tr[rt];
        }
        push_down(rt);
        treenode ans;
        int mid = l + r >> 1;
        if (mid >= L)
            ans = merge(rt, ans, query(lson, l, mid, L, R));
        if (mid < R)
            ans = merge(rt, ans, query(rson, mid + 1, r, L, R));
        return ans;
    }
}tr[2];

void solve() {
    std::cin >> n >> m;
    std::cin >> s;
    s = ' ' + s;

    p[0] = 1;
    for (int i = 1; i <= n; i++) {
        p[i] = p[i - 1] * base % mod;
        for (int j = 0; j < 26; j++) {
            ha[i][0][j] = (s[i] == ('a' + j));
            ha[n - i + 1][1][j] = ha[i][0][j];
        }
    }
    tr[0].init(n, 0);
    tr[1].init(n, 1);

    while(m--) {
        int op;
        std::cin >> op;
        if (op == 1) {
            int l, r;
            std::cin >> l >> r;

            auto v1 = tr[0].query(1, 1, n, l, r);
            auto v2 = tr[1].query(1, 1, n, n - r + 1, n - l + 1);
            
            bool flag = 1;
            for (int i = 0; i < 26; i++) {
                flag &= (v1.vis[i] == v2.vis[i]);
            }
            if (flag) {
                std::cout << "YES" << '\n';
            }
            else {
                std::cout << "NO" << '\n';
            }
        }
        else {
            int l, r, x;
            std::cin >> l >> r >> x;

            x %= 26;
            if (x == 0) continue;

            tr[0].update(1, 1, n, l, r, x);
            tr[1].update(1, 1, n, n - r + 1, n - l + 1, x);
        }
    }
}  

signed main() {
    std::ios::sync_with_stdio(0);
    std::cout.tie(0);
    std::cin.tie(0);

    i64 t = 1; 
    // std::cin >> t;
    while (t--) {
        solve();
    }
}

全部评论
D可以O(n)
点赞 回复 分享
发布于 2024-09-28 22:18 河北
请问c题的vis数组是干什么的
点赞 回复 分享
发布于 2024-09-28 13:44 湖南
第二题感觉出的矛盾,不允许所有人做同一题,当只有一个人的时候,怎么分配都不满足条件,因为它就是全集
点赞 回复 分享
发布于 2024-09-28 09:38 吉林

相关推荐

纯朴的商业竞争手段
职场不咸鱼:看来商家也喜欢jd
投递美团等公司6个岗位 > 京东美团大战,你怎么看?
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
昨天 16:41
妈妈,我被应届生身份压得喘不过气,真的好想你们。我不知道该往哪走,也不知道该怎么努力,甚至开始怀疑是不是自己根本就不够聪明。从小,大家都说我是个笨小孩,只有拼尽全力才能读好书。我很少像其他孩子那样肆意玩耍,因为转学到乡村小学,连方言都不会说,别人说什么我都傻乎乎地相信。但我一直努力着,从乡村小学到初中尖子生,考上重点高中,选择文科,进入&nbsp;985&nbsp;高校,再到转专业,参加演出、担任学生干部。本想努力保研,却被疫情打乱计划,那段日子,我仿佛被黑暗笼罩,花了好久才走出来。后来我积极参加比赛、大创项目,大量阅读,还以为自己有文学天赋,于是决定考研。我想去上海,想去看看更大的世界,想用自己的文字为那些真诚...
wuwuwuoow:工作可以慢慢找,因为急也没用。根据我的经验,工作主要还是看运气,慢慢来吧。比起找工作,我觉得更重要的是调整下心态。我就直说吧,你目前状态肯定找不到工作,感觉处于崩溃边缘了。 现在有两种选择:一种是找不到工作每天又很焦虑,一种是找不到工作但每天很开心。你选择哪个呢? 可能会觉得奇怪,没工作怎么会开心呢?至少对我而言,工作很重要,但绝对不是生活的全部。你有摄影和画画的兴趣爱好,这点已经要比绝大部分人强了。很多人虽然有工作,或者在读硕士博士,但他们唯一的“兴趣爱好”就是玩手机。你的精神世界至少比我要丰富,我每天只会玩手机。我也想学画画,但又不想动手去学,哈哈。 我相信你一开始学摄影和画画,不是为了赚钱才去学的,而是因为自己喜欢才去学的。所以没有必要这么功利,非得从兴趣爱好里赚钱。因为和钱相关的事情,不叫兴趣爱好,这叫工作,工作怎么会让人快乐?所以纯粹点,把兴趣爱好当成兴趣爱好本身,作为一种休闲娱乐的方式,不要想着从里面赚钱。就像我前面所说的一样,工作不是生活的全部。 平时的话,可以多陪妈妈聊聊天,妈妈肯定不指望你赚大钱,只希望你每天开开心心。就像最后只能回家当老师,又有什么不好的呢,可以多花时间陪陪家人。 也可以多试试冥想,闭上眼,专注呼吸,学会“活在当下”。没必要对过去的事情后悔,没必要对未来没发生的事情焦虑。因为你没法改变他们,你只能专注眼前的事情。 新疆的教育资源不算好,你能考上 985 足以证明你比大部分学习能力强了,所以你很聪明,你根本就不笨。只是你需要试试接纳自己,接纳自己是个普通人,接纳自己不可能什么都会,接纳自己也会犯错,也要接纳比自己更强的人存在。这才是实实在在的你。 所以,我建议你可以给自己休息一个月,调整一下心情。旅游,冥想,运动,规律作息。等心情好了,再来烦恼下工作的事情,好吗? 加油哦
点赞 评论 收藏
分享
03-29 14:19
门头沟学院 Java
你背过凌晨4点的八股文么:加油同学,人生的容错率很高,只是一个暑期罢了,后面还有很多机会!
点赞 评论 收藏
分享
评论
24
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务