6.28 考试改题

P7424 [THUPC 2017] 天天爱射击

很经典的整体二分,注意正难则反。

另外需要精确地判断二分边界。

#include <bits/stdc++.h>
#define int long long
#define pb emplace_back
using namespace std;

const int N = 2e5 + 5;
int n, m, l[N << 1], r[N << 1], k[N << 1], x[N << 1], op[N << 1], ans[N << 1], res[N];
vector<int> start;

namespace BIT {
    int t[N];

    inline int lowbit(int x) {
        return x & -x;
    }

    inline void add(int x, int k) {
        for( ; x <= n ; x += lowbit(x))
            t[x] += k;

        return ;
    }

    inline int ask(int x) {
        int res = 0;

        for( ; x ; x -= lowbit(x))
            res += t[x];

        return res;
    }

    inline int query(int x, int y) {
        return ask(y) - ask(x - 1);
    }
}

using namespace BIT;

inline void solve(int L, int R, vector<int> q) {
    if(q.empty()) return ;

    if(L == R) {
        for(auto i : q) ans[i] = L;

        return ;
    }

    int mid = (L + R) >> 1;
    vector<int> ql, qr;

    for(auto i : q) {
        if(op[i] == 1) {
            if(i <= mid) {
                ql.pb(i);
                add(x[i], 1);
            }
            else qr.pb(i);
        }
        else {
            int v = query(l[i], r[i]);

            if(k[i] <= v) ql.pb(i);
            else {
                qr.pb(i);
                k[i] -= v;
            }
        }
    }

    for(auto i : ql)
        if(op[i] == 1) add(x[i], -1);

    solve(L, mid, ql), solve(mid + 1, R, qr);

    return ;
}

signed main() {
    ios_base :: sync_with_stdio(NULL);
    cin.tie(nullptr);
    cout.tie(nullptr);

    cin >> n >> m;
    for(int i = 1 ; i <= n ; ++ i)
        cin >> l[i + m] >> r[i + m] >> k[i + m];
    for(int i = 1 ; i <= m ; ++ i)
        cin >> x[i], op[i] = 1;

    for(int i = 1 ; i <= n + m ; ++ i)
        start.pb(i);

    solve(0, m + 1, start);

    for(int i = 1 ; i <= n + m ; ++ i)
        if(! op[i]) ++ res[ans[i]];

    for(int i = 1 ; i <= m ; ++ i)
        cout << res[i] << '\n';

    return 0;
}

CF1430E String Reversal

好像大家考试都过了这一题,但我实在是一点都没想到。

“交换邻项”这一操作,考虑从逆序对下手。

一个经典套路是将最终态下标赋予一个递增序列,然后将其和原先相同字符的位置一一对应。

因为是要操作次数最少,那么逆序对数也应该最少。即原序列应尽量保持递增。

那么按照这个思路贪心地操作即可。

#include <bits/stdc++.h>
#define int long long
#define pb emplace_back
using namespace std;

const int N = 2e5 + 5;
int n, a[N], p[N], pos[N], cnt[N];
vector<int> v[N];
string s;

namespace BIT {
    int t[N];

    inline int lowbit(int x) {
        return x & -x;
    }

    inline void add(int x, int k) {
        for( ; x <= n ; x += lowbit(x))
            t[x] += k;

        return ;
    }

    inline int query(int x) {
        int res = 0;

        for( ; x ; x -= lowbit(x))
            res += t[x];

        return res;
    }
}

using namespace BIT;

inline int to(char ch) {
    return ch - 'a' + 1;
}

signed main() {
    ios_base :: sync_with_stdio(NULL);
    cin.tie(nullptr);
    cout.tie(nullptr);

    cin >> n >> s;
    s = " " + s;

    for(int i = n ; i ; -- i) {
        p[i] = n - i + 1;
        v[to(s[i])].pb(p[i]);
        ++ cnt[to(s[i])];
    }

    for(int i = 1 ; i <= n ; ++ i)
        a[i] = v[to(s[i])][pos[to(s[i])] ++];

    int ans = 0;

    // cerr << "!";

    for(int i = n ; i ; -- i) {
        // cerr << "!";

        // cerr << a[i];

        ans += query(a[i] - 1);

        add(a[i], 1);
    }

    cout << ans;

    return 0;
}
posted @ 2025-06-28 16:08  endswitch  阅读(5)  评论(0)    收藏  举报