河南萌新联赛第(三)场,个人题解

发工资咯

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

A

解题思路:

考虑线段树维护如下信息:


  • sum1sum_1 表示区间 [l,r][l,r] 中每个人目前的月工资的总和。

  • lazy1lazy_1 表示 sum1sum_1 的懒标记。

  • sum2sum_2 表示区间 [l,r][l,r] 中每个人目前所有 qq 个月的工资总和。

    这里我们采用提前发放工资的想法,我直接维护每个人最后能获得的工资是多少。

  • lazy2lazy_2 表示 sum2sum_2 的懒标记。


我们考虑第一个操作,我们发现给区间 [l,r][l,r] 的工资上涨 ww

首先相当于给区间中每个人的月工资都加上 ww

那么我们再考虑对于区间中每个人目前所有 qq 个月的工资总和应该怎么变。

由于我们工资上涨了,那么从当前月到后面的月份,我的工资都会多出 ww

若当前是第 ii 月,则我们发现区间中每个人 qq 个月的总工资都会增加:(qi+1)×w(q-i+1)\times w。

那么现在我们需要询问 [l,r][l,r] 中每个人前 ii 个月的工资总和 ansans 是多少。

不难得出: ans=sum2(qi)×sum1ans=sum_2-(q-i)\times sum_1

意思是我们若到第 ii 天我们的月工资总和是 sum1sum_1 ,那么接下来剩下的 (qi)(q-i) 个月我们每个月都会多出这 sum1sum_1,所以我们需要用总的 qq 个月的工资减去这部分,即可得到前 ii 个月的。

分析到此,就变成了维护两个信息,两个标记的区间加线段树板子题了。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N = 2e5 + 10, mod = 998244353;
struct node {
    int l, r;
    LL s[2], lazy[2];
}tr[4 * N];

void pushup(int u) {
    tr[u].s[0] = (tr[u << 1].s[0] + tr[u << 1 | 1].s[0]) % mod;
    tr[u].s[1] = (tr[u << 1].s[1] + tr[u << 1 | 1].s[1]) % mod;   
}

void pushdown(int u, int t) {
    if (tr[u].lazy[t]) {
        tr[u << 1].s[t] += (tr[u << 1].r - tr[u << 1].l + 1) * tr[u].lazy[t] % mod;
        tr[u << 1 | 1].s[t] += (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1) * tr[u].lazy[t] % mod;
        tr[u << 1].lazy[t] += tr[u].lazy[t];
        tr[u << 1 | 1].lazy[t] += tr[u].lazy[t];   
        tr[u << 1].s[t] %= mod;
        tr[u << 1 | 1].s[t] %= mod;
        tr[u << 1].lazy[t] %= mod;
        tr[u << 1 | 1].lazy[t] %= mod;
        tr[u].lazy[t] = 0;
    }
}

void build(int u, int l, int r) {
    tr[u] = {l, r};
    if (l == r) return ;
    int mid = (l + r) >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

void modify(int u, int l, int r, LL w, int t) {
    if (tr[u].l >= l && tr[u].r <= r) {
        tr[u].lazy[t] = (tr[u].lazy[t] + w) % mod;
        tr[u].s[t] = (tr[u].s[t] + w * (tr[u]. r - tr[u].l + 1) % mod) % mod;
        return; 
    }
    pushdown(u, t);
    int mid = (tr[u].l + tr[u].r) >> 1;
    if (l <= mid) modify(u << 1, l, r, w, t);
    if (r > mid) modify(u << 1 | 1, l, r, w, t);
    pushup(u);
}

LL query(int u, int l, int r, int t) {
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].s[t];
    pushdown(u, t);
    int mid = (tr[u].l + tr[u].r) >> 1;
    LL res = 0;
    if (l <= mid) res = (res + query(u << 1, l, r, t)) % mod;
    if (r > mid) res = (res + query(u << 1 | 1, l, r, t)) % mod;
    return res;
}

int main() {
    cin.tie(nullptr)->ios::sync_with_stdio(false);
    int n, q;
    cin >> n >> q;
    build(1, 1, n);
    for(int i = 1; i <= q; i++) {
        int op;
        cin >> op;
        if (op == 0) {
            int l, r, w;
            cin >> l >> r >> w;
            modify(1, l, r, w % mod, 0);
            modify(1, l, r, (q - i + 1LL) * w % mod, 1);
        } else {
            int l, r;
            cin >> l >> r;
            LL s1 = query(1, l, r, 1);
            LL s2 = query(1, l, r, 0) * (q - i) % mod;
            cout << ((s1 - s2) % mod + mod) % mod << '\n';
        }
    }

    return 0;
}

C

解题思路

要求满足 A×B+C×D=nA\times B+C\times D=n 的四元组 (A,B,C,D)(A,B,C,D) 的数量,首先我们可以把 A×BA\times BC×DC\times D 分别看成两个整体,

我们不妨设 x=A×Bx=A\times By=C×By=C\times B

f(x)f(x) 表示满足 x=u×vx=u\times v 的二元组 (u,v)(u,v) 的数量,那么我们可以枚举 xx 的取值,那么自然 y=nxy=n-x

故我们得到了 ans=x=1n1f(x)×f(nx)ans=\sum_{x=1}^{n - 1}f(x)\times f(n - x)

这样我们就把四元组问题,转成了二元组,现在我们重点讨论一下如何求解 f(x)f(x)

x=u×vx=u\times v,那么我们可以知道 u,vu,v 必须都是 xx 的因子,那么记 tottot 表示 xx 的因子个数,那么 f(x)=totf(x)=tot

比如 x=8x=8,其因子为:1,2,4,81,2,4,8 ,个数为 44,那么一共就有 44 种不同的二元组满足条件:(1,8),(8,1),(2,4),(4,2)(1,8),(8,1),(2,4),(4,2)

据此问题转化为了求解 xx 的因子个数,由于 1n1051\leq n\leq 10^5 ,故我们可以采用如下两种方法求解: 方法1:试除法,复杂度是 O(nn)O(n\sqrt n)

方法2:调和级数复杂度枚举 ,复杂度是 O(nlogn)O(nlogn)

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef __int128 i128;

int main() {
    cin.tie(nullptr)->ios::sync_with_stdio(false);
    int n;
    cin >> n;
    vector<int> f(n + 1);
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j += i) {
            f[j]++;
        }
    }
    LL ans = 0;
    for (int i = 1; i < n; i++) {
        ans += 1LL * f[i] * f[n - i];
    }
    cout << ans << '\n';

    return 0;
}

D

解题思路

  • x&y=ax\&y=a
  • x+y=sx+y=s

对于此类问题,我们通常需要把数进行二进制展开,一位位看。

那么对于 aa 的第 ii 位,如果它是 11,那么就意味这 xxyy 的第 ii 位都要为 11

给出一个等式:x+y=xy+2×(x&y)x+y=x\bigoplus y +2\times(x\&y)

那么如果 s<2×as<2\times a ,一定是无解,因为 xyx\bigoplus y 不可能弄出负数

否则记 b=xy=x+y2×(x&y)=s2×ab=x\bigoplus y=x+y-2\times (x\&y)=s-2\times a

我们继续看 bb 的第 ii 位,如果第 ii 为是 11,且此时 xxyy 的第 ii 为都是 11,此时发生矛盾,因为这样

xyx\bigoplus y 在第 ii 位的结果一定是 00;那么如果 bb 的第 ii 位是 00,无论 xxyy 这一位是否被确定,我们都可以构造一个合法的组合使得它成立。例如当 bb 的第 ii 位是 00,那么 xxyy 的第 ii 位可以是 (1,1),(0,0)(1,1),(0,0);若当 bb 的第 ii 位是 00,那么 xxyy 的第 ii 位可以是 (1,0),(0,1)(1,0),(0,1)

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef __int128 i128;

void solve() {
    LL a, s;
    cin >> a >> s;
    if (s < (i128)2 * a) {
        cout << "No\n";
        return ;
    }
    vector<int> x(61, -1), y(61, -1);
    LL b = s - 2 * a; // x ^ y
    for (int i = 0; i <= 60; i++) {
        if (a >> i & 1) {
            x[i] = y[i] = 1;
        }
    }
    for (int i = 0; i <= 60; i++) {
        int u = b >> i & 1;
        if (x[i] == 1 && y[i] == 1 && u) {
            cout << "No\n";
            return ;
        }
    }
    cout << "Yes\n";
}

int main() {
    cin.tie(nullptr)->ios::sync_with_stdio(false);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}
全部评论

相关推荐

03-18 09:45
莆田学院 golang
牛客749342647号:佬,你这个简历模板是哪个,好好看
点赞 评论 收藏
分享
真得找个班上:真是白嫖学生劳动力脸都不要了
点赞 评论 收藏
分享
好消息是活的像个人了,周末可以约会吃饭打游戏了坏消息是钱没了,当初来小红书就是为了钱啊哭笑不得😭
犯困嫌疑人:好事儿啊,取消大小周能有更多自己的时间,周末还能约对象玩,这不美滋滋?
投递小红书等公司6个岗位 > 小红书取消大小周
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务