牛客练习赛48 部分题解

B. 小w的a=b问题

思路:把阶乘hash就行
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10, N = 1e5;
const int mod1 = 1e9 + 7, mod2 = 998244353;
ll p1[maxn], p2[maxn];
int main() {
    p1[0] = p2[0] = 1;
    for (int i = 1; i <= N; i++) {
        p1[i] = p1[i - 1] * i % mod1;
        p2[i] = p2[i - 1] * i % mod2;
    }
    int T;
    cin>>T;
    while (T--) {
        int n, m, x;
        ll s1 = 1, s2 = 1;
        cin>>n>>m;
        for (int i = 1; i <= n; i++) {
            cin>>x;
            s1 *= p1[x]; s1 %= mod1;
            s2 *= p2[x]; s2 %= mod2;
        }
        ll s3 = 1, s4 = 1;
        for (int i = 1; i <= m; i++) {
            cin>>x;
            s3 *= p1[x]; s3 %= mod1;
            s4 *= p2[x]; s4 %= mod2;
        }
        if (s1 == s3 && s2 == s4)
            puts("equal");
        else
            puts("unequal");
    }
}

C. 小w的糖果

我们维护一下差分数组sum,操作:1 x 就是sum[x]++,操作:2 x 就是sum[x] 到 sum[n] +=1,操作3不好搞了,我们记S数组也是差分数组,不过假设S[i] = x,那么S[i] 真正的值是(2 * i - 1) * x,那么操作:3 x 可以这样转化:S[x] 到 S[n] +=1,sum[x] 到 sum[n] -= 2 * (x - 1),这一步你们自己推一推,我们发现,对于差分数组区间加减法,可以再进行一次差分,差分套差分O(n)搞定这题
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10, mod = 1e9 + 7;
ll sum[2][maxn], a[maxn];
void add(ll &x, ll y) {
    x += y;
    if (x >= mod)
        x -= mod;
    if (x < 0)
        x += mod;
}
int main() {
    int T;
    cin>>T;
    while (T--) {
        int n, q, opt, x;
        cin>>n>>q;
        for (int i = 1; i <= n; i++)
            for (int j = 0; j < 2; j++)
                sum[j][i] = 0;
        while (q--) {
            cin>>opt>>x;
            if (opt == 1)
                add(sum[0][x], 1), add(sum[0][x + 1], -1);
            else if (opt == 2)
                add(sum[0][x], 1);
            else {
                add(sum[1][x], 1);
                add(sum[0][x], -(x - 1) * 2);
            }
        }
        for (int i = 1; i <= n; i++) {
            add(sum[0][i], sum[0][i - 1]);
            add(sum[1][i], sum[1][i - 1]);
            a[i] = sum[0][i] + sum[1][i] * (2 * i - 1) % mod;
            a[i] %= mod;
            add(a[i], 0);
        }
        for (int i = 1; i <= n; i++) {
            add(a[i], a[i - 1]);
            printf("%lld", a[i]);
            if (i != n)
                printf(" ");
            else
                puts("");
        }
    }
}

E. 小w的矩阵前k大元素

思路:对于每一次查询,我们先用两个set:s1,s2 维护当前a数组前x个元素和b数组前y个元素,并且用一颗权值线段树维护b数组前y个元素,如果要查询前k小的元素,我们二分求第k小的元素mid,判断方法:枚举第一个set的元素x,从权值线段树中找 <= mid - x的元素有多少个,然后和k对比就行。假设找到了mid = ans,那么我们再枚举两个set的元素 t1, t2,把所有满足: t1 + t2 < mid 的元素加入优先队列,然后输出就行,复杂度:klognlogn
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10, M = 1e9;
ll a[maxn], b[maxn], N = 4e9;;
multiset<ll> s1, s2;
priority_queue<ll, vector<ll>, greater<ll> > Q;
int sum[maxn * 40], ls[maxn * 40], rs[maxn * 40];
int cnt, rt;
void up(int& o, ll l, ll r, ll k) {
    if (!o)
        o = ++cnt;
    sum[o]++;
    if (l == r)
        return;
    ll m = (l + r) / 2;
    if (k <= m)
        up(ls[o], l, m, k);
    else
        up(rs[o], m + 1, r, k);
}
int qu(int o, ll l, ll r, ll k) {
    if (r <= k)
        return sum[o];
    ll m = (l + r) / 2;
    int res = qu(ls[o], l, m, k);
    if (k > m)
        res += qu(rs[o], m + 1, r, k);
    return res;
}
int ok(ll T, int k) {
    int res = 0;
    for (auto x : s1) {
        if (T - x < 0)
            return 0;
        int tmp = qu(rt, 0, N, T - x);
        if (!tmp)
            return 0;
        res += tmp;
        if (res >= k)
            return 1;
    }
    return 0;
}
int main() {
    int n, m, q;
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &a[i]), a[i] += M;
    for (int i = 1; i <= m; i++)
        scanf("%lld", &b[i]), b[i] += M;
    int x = 1, y = 1, d;
    s1.insert(a[1]);
    s2.insert(b[1]);
    up(rt, 0, N , b[1]);
    char s[2];
    while (q--) {
        scanf("%s%d", s, &d);
        if (s[0] == 'R') {
            for (int i = y + 1; i <= min(m, y + d); i++)
                s2.insert(b[i]), up(rt, 0, N, b[i]);
            y += d;
            y = min(y, m);
        }
        else if(s[0] == 'D') {
            for (int i = x + 1; i <= min(n, x + d); i++)
                s1.insert(a[i]);
            x += d;
            x = min(x, n);
        }
        else {
            ll l = 0, r = N;
            while (l < r) {
                ll m = (l + r) / 2;
                if (ok(m, d))
                    r = m;
                else
                    l = m + 1;
            }
            int res = 0;
            for (auto t1 : s1) {
                int flag = 0;
                for (auto t2 : s2) {
                    if (t1 + t2 < l)
                        Q.push(t1 - M + t2 - M), flag = 1;
                    else
                        break;
                }
                if (!flag)
                    break;
            }
            while (!Q.empty() && d) {
                printf("%lld", Q.top());
                Q.pop();
                d--;
                if (d)
                    printf(" ");
            }
            while (d--) {
                printf("%lld", l - M * 2);
                if (d)
                    printf(" ");
            }
            puts("");
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

长沙橘子猫

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

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

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

打赏作者

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

抵扣说明:

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

余额充值