「Fortuna OJ」Mar 4th – Group A 解题报告

A – 漆黑列车载运数个谎言

这道题应该是并查集域的裸题,不讲。

// A.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6 + 200;
int n, m, fa[MAX_N << 2];
int find(int x) { return fa[x] == x ? fa[x] : fa[x] = find(fa[x]); }
void unite(int x, int y) { fa[find(x)] = fa[find(y)]; }
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= 2 * n; i++)
        fa[i] = i;
    while (m--)
    {
        int opt, x, y;
        scanf("%d%d%d", &opt, &x, &y);
        if (opt == 0)
            unite(x, y), unite(x + n, y + n);
        else if (opt == 1 || opt == 2)
            unite(x, y + n), unite(x + n, y);
        else if (find(x) == find(y) || find(x + n) == find(y + n))
            puts("1");
        else
            puts("0");
    }
    return 0;
}

B – 神在夏至祭降下了神谕

傻逼都知道这道题的方程为:

\[ f[i] = \sum_{[i,j] \in validSet} f[j] \]

然而这样枚举是\(O(n^2)\)的,非常垃圾的复杂度。我们可以用树状数组来搞定这个转移,这样的话复杂度就是\(O(n log n)\)的。首先,绝对差的表达式是\(|numOfSummer – numOfWinter|\)。我们可以考虑求个前缀和,把每一个夏人的贡献设为\(1\),冬人的贡献设为\(-1\),所以方程中要求的\(f[j]\)集其实就是在区间\([-k,k]\)中,然后多加一个人就相当于把整个区间向右\左平移了,所以真正的区间就是\([sum[i]-k,sum[i]+k]\),然后计算完毕之后直接往树状数组的\(sum[i]\)处把\(f[i]\)塞进去即可。

// B.cpp
#include <bits/stdc++.h>
#define lowbit(x) (x & -x)
#define ll long long
using namespace std;
const int MAX_N = 2e5 + 2000, mod = 1e9 + 7;
ll tree[MAX_N << 2], f[MAX_N], n, prefix[MAX_N], tmpx, k;
// the number domain still exists;
void add(int x, ll d)
{
    x += MAX_N;
    while (x <= 2 * MAX_N)
        tree[x] += d, x += lowbit(x);
}
ll sum(int x)
{
    x += MAX_N;
    ll ans = 0;
    while (x)
        ans += tree[x], x -= lowbit(x);
    return ans;
}
int main()
{
    scanf("%lld%lld", &n, &k);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &tmpx), prefix[i] = prefix[i - 1] + ((tmpx == 1) ? 1 : -1);
    add(0, 1);
    for (int i = 1; i <= n; i++)
        f[i] = (sum(prefix[i] + k) - sum(prefix[i] - k - 1) + mod) % mod, add(prefix[i], f[i]);
    printf("%lld", f[n]);
    return 0;
}

C – 被粉碎的线段树

先有一个结论:

区间定位个数(答案) = 2 * 区间长度 – 完全被该区间包含的节点个数。——题解

所以,我们只需要搞定后面那个被完全包含的节点个数统计即可。从数学上讲,我们要统计区间\([l’,r’],l’\geq l, r’ \leq r\)的个数。明显的,这他妈就是一个二维偏序问题。所以,题目结束。

// C.cpp
#include <bits/stdc++.h>
#define ll long long
#define lowbit(x) (x & -x)
#define pr pair<int, int>
using namespace std;
const int MAX_N = 1e5 + 2000;
int n, m, tmpx, tmpy1, tot, tree[MAX_N << 2];
pr nodes[MAX_N << 2];
struct query
{
    int l, r, ans, id;
    bool operator<(const query &q) const { return id < q.id; }
} queries[MAX_N];
bool cmp(pr a, pr b) { return a.second < b.second; }
bool cmp2(query a, query b) { return a.r < b.r; }
void init(int l, int r)
{
    if (l == r)
    {
        nodes[++tot].first = l, nodes[tot].second = r;
        return;
    }
    nodes[++tot].first = l, nodes[tot].second = r;
    int mid;
    scanf("%d", &mid);
    init(l, mid), init(mid + 1, r);
}
void add(int x, int d)
{
    while (x <= n)
        tree[x] += d, x += lowbit(x);
}
ll sum(int x)
{
    ll ans = 0;
    while (x)
        ans += tree[x], x -= lowbit(x);
    return ans;
}
int main()
{
    scanf("%d%d", &n, &m);
    init(1, n);
    sort(nodes + 1, nodes + 1 + tot, cmp);
    for (int i = 1; i <= m; i++)
        scanf("%d%d", &queries[i].l, &queries[i].r), queries[i].id = i;
    sort(queries + 1, queries + 1 + m, cmp2);
    int cur = 1;
    for (int i = 1; i <= m; i++)
    {
        int xlimit = queries[i].r;
        while (nodes[cur].second <= xlimit && cur <= tot)
            add(nodes[cur].first, 1), cur++;
        queries[i].ans = sum(queries[i].r) - sum(queries[i].l - 1);
    }
    sort(queries + 1, queries + 1 + m);
    for (int i = 1; i <= m; i++)
        printf("%lld\n", 2 * (queries[i].r - queries[i].l + 1) - queries[i].ans);
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *