比赛-CioCio学长的训练赛 (Aug 18, 2018)

1.) wjj 的子集序列

暴力二进制枚举子集打表,然后可以发现答案就是最大值。根据公式推一波也行吧……

#include <cstdio>
#include <stack>
#include <ctype.h>

using namespace std;

typedef long long ll;

template<typename T>
void rd(T &num)
{
    char tt;
    bool flag = 0;
    while (!isdigit(tt = getchar()) && tt != '-');
    if (tt == '-') num = 0, flag = 1;
    else num = tt - '0';
    while (isdigit(tt = getchar()))
        num = num * 10 + tt - '0';
    if (flag) num = -num;
    return;
}

template<typename T>
void pt(T num)
{
    if (num < 0) putchar('-'), num = -num;
    stack<char> SSS;
    do SSS.push(num % 10 + '0');
    while (num /= 10);
    while (!SSS.empty())
        putchar(SSS.top()), SSS.pop();;
    putchar('\n');
    return;
}

ll ans;

int main()
{
    freopen("subset.in", "r", stdin);
    freopen("subset.out", "w", stdout);
    
    int N;
    rd(N);
    for (int i = 1; i <= N; ++i) {
        ll t;
        rd(t);
        if (i == 1 || ans < t) ans = t;
    }
    pt(ans);
    return 0;
}

2.) wjj 的排列序列

没有限制条件时,显然最优排列是一个单调下降的排列。比如 $ {5, 4, 3, 2, 1} $ 。只有一个限制条件时,比如要求 \(5\)\(2\) 之后,最优排列是 $ {4, 3, 2, 5, 1} $ 。分析一下可以发现应该尽量把较大的数放在前面,当然是在保证满足限制条件的情况下。简单反证一下,如果不尽量把较大的放在前面,有的数字就会被前面较小的数“压制”,前缀最小值就会变小,所以不行。为了满足限制条件,可以写一个类似 Top-sort 的东西。

#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
#include <stack>
#include <ctype.h>

using namespace std;

template<typename T>
void rd(T &num)
{
    char tt;
    bool flag = 0;
    while (!isdigit(tt = getchar()) && tt != '-');
    if (tt == '-') num = 0, flag = 1;
    else num = tt - '0';
    while (isdigit(tt = getchar()))
        num = num * 10 + tt - '0';
    if (flag) num = -num;
    return;
}

template<typename T>
void pt(T num)
{
    if (num < 0) putchar('-'), num = -num;
    stack<char> SSS;
    do SSS.push(num % 10 + '0');
    while (num /= 10);
    while (!SSS.empty())
        putchar(SSS.top()), SSS.pop();
    putchar('\n');
    return;
}

typedef long long ll;

const int _N = 120000;
const int INF = 1e9;

priority_queue<int> Q;
vector<int> G[_N];
int cnt[_N];

int main()
{
    int N, M;
    rd(N), rd(M);
    for (int a, b, i = 1; i <= M; ++i) {
        rd(a), rd(b);
        G[a].push_back(b);
        ++cnt[b];
    }
    for (int i = 1; i <= N; ++i)
        if (!cnt[i]) Q.push(i);
    int mn = INF;
    ll ans = 0;
    while (!Q.empty()) {
        int t = Q.top();
        Q.pop();
        mn = min(mn, t);
        ans += mn;
        for (int i = G[t].size() - 1; i >= 0; --i)
            if (!--cnt[G[t][i]]) Q.push(G[t][i]);
    }
    pt(ans);
    return 0;
}

3.) wjj 的零一序列

这道题类似 wxh 学长讲过的一道 Codeforces 的题 Queries 。CF 的那道题更加复杂,需要把每个数按二进制拆位,然后用位数那么多棵线段树维护区间信息。这个拆位思想其他题也遇到过,非常有用!另外 wxh 大佬真强啊……当时讲了十多道题,现在已经在其他地方遇到过两道几乎一致的题啦。可能因为 CF 的题,大家都觉得改改就能重新再出一道题吧……
这题先对原序列求前缀异或序列,对后者维护一下区间 0 和 1 的数量,记为 $ v_0, v_1 $ ,然后询问 0 的答案是 $ v_0 \cdot (v_0 - 1) / 2 + v_1 \cdot (v_1 - 1) / 2 $ ,询问 1 的答案是 $ v_0 \cdot v_1 $ 。注意询问原序列 $ [x, y] $ 区间时对应询问的前缀异或序列区间是 $ [x - 1, y] $ 。最后改悔一下,线段树动态开点要注意随时新建儿子节点,考试的时候用到还没建立的儿子节点的信息,更新了当前节点,然后就爆 0 了 OrzOrz 。

#include <cstdio>
#include <algorithm>
#include <stack>
#include <ctype.h>

using namespace std;

#define SC(a, b) (static_cast<a>(b))

template<typename T>
void rd(T &num)
{
    char tt;
    bool flag = 0;
    while (!isdigit(tt = getchar()) && tt != '-');
    if (tt == '-') num = 0, flag = 1;
    else num = tt - '0';
    while (isdigit(tt = getchar()))
        num = num * 10 + tt - '0';
    if (flag) num = -num;
    return;
}

template<typename T>
void pt(T num)
{
    if (num < 0) putchar('-'), num = -num;
    stack<char> SSS;
    do SSS.push(num % 10 + '0');
    while (num /= 10);
    while (!SSS.empty())
        putchar(SSS.top()), SSS.pop();
    putchar('\n');
    return;
}

typedef long long ll;

const int _N = 5000000;

struct data {
    int v0, v1;
    data(int v0 = 0, int v1 = 0):
        v0(v0), v1(v1) { }
};

int Lazy[_N], L[_N], R[_N], V0[_N], V1[_N];
int Rt, Cnt, N, TTT;

void putdown(int &p, int l, int r)
{
    int mid = (l + r) >> 1;
    
    swap(V0[L[p]], V1[L[p]]), swap(V0[R[p]], V1[R[p]]);
    Lazy[p] ^= 1, Lazy[L[p]] ^= 1, Lazy[R[p]] ^= 1;
    return;
}

void modify(int &p, int l, int r, int s, int t)
{
    if (!p) p = ++Cnt, V0[p] = r - l + 1;
    if (s <= l && r <= t) {
        swap(V0[p], V1[p]);
        Lazy[p] ^= 1;
        return;
    }
    int mid = (l + r) >> 1;
    if (!L[p]) L[p] = ++Cnt, V0[L[p]] = mid - l + 1;
    if (!R[p]) R[p] = ++Cnt, V0[R[p]] = r - mid;
    if (Lazy[p])
        putdown(p, l, r);
    if (t >= l && s <= mid)
        modify(L[p], l, mid, s, t);
    if (t > mid && s <= r)
        modify(R[p], mid + 1, r, s, t);
    V0[p] = V0[L[p]] + V0[R[p]];
    V1[p] = V1[L[p]] + V1[R[p]];
    return;
}

data query(int &p, int l, int r, int s, int t)
{
    if (!p) p = ++Cnt, V0[p] = r - l + 1;
    if (s <= l && r <= t) {
        return data(V0[p], V1[p]);
    }
    int mid = (l + r) >> 1, s0 = 0, s1 = 0;
    if (!L[p]) L[p] = ++Cnt, V0[L[p]] = mid - l + 1;
    if (!R[p]) R[p] = ++Cnt, V0[R[p]] = r - mid;
    if (Lazy[p])
        putdown(p, l, r);
    if (t >= l && s <= mid) {
        data tmp = query(L[p], l, mid, s, t);
        s0 += tmp.v0, s1 += tmp.v1;
    }
    if (t > mid && s <= r) {
        data tmp = query(R[p], mid + 1, r, s, t);
        s0 += tmp.v0, s1 += tmp.v1;
    }
    return data(s0, s1);
}

int main()
{
    rd(N);
    for (int t, i = 1; i <= N; ++i) {
        rd(t);
        if (t) modify(Rt, 0, N, i, N);
    }
    rd(TTT);
    while (TTT--) {
        int ins, x, y;
        rd(ins), rd(x);
        if (ins == 2) {
            modify(Rt, 0, N, x, N);
        } else {
            rd(y);
            data t = query(Rt, 0, N, x - 1, y);
            if (ins == 0) {
                ll s = 0;
                if (t.v0 >= 2) s += SC(ll, t.v0) * (t.v0 - 1) / 2;
                if (t.v1 >= 2) s += SC(ll, t.v1) * (t.v1 - 1) / 2;
                pt(s);
            } else {
                pt(SC(ll, t.v0) * t.v1);
            }
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ghcred/p/9497157.html
今日推荐