362間隔をAcWing

彼は練習の貪欲+データ構造に関する書籍を聞いて、少し勉強していると述べました。

貪欲な平原

右端点に全ての線分を考える\(B \)小から大まで、各ラインセグメントの要件を考慮してください。

  • あなたは既に要件を満たしている場合スキップ
  • それ以外の場合は、(なぜならこのセグメントの権利なので、高い耐障害性のすべてのセグメントの後に右エンドポイント)後方の数を選択してみてください

したがって、我々は、アレイを構築することができ、\(D [I] \)を表し、(I \)\数が選択されたか否か(フィル\(1 \)または\(0 \) )、オーバースイープ([L、R] \ \ )間隔の和、そして数は貪欲から前方に置くことができます。

各セグメントに対して必要\(O(R&LT - 1 + L)\) したがって、最悪の場合には\(O(^ N-2)\) しかし、簡単に\(52ms \)の前に。

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 50005;
int n, d[N], c[N];
struct Seg{
    int a, b, c;
    bool operator < (const Seg &x) const {
        return b < x.b;
    }
}e[N];
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].c);
    sort(e + 1, e + 1 + n);
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int l = e[i].a, r = e[i].b, cnt = e[i].c;
        for (int j = l; j <= r; j++)
            cnt -= d[j];
        if(cnt > 0) {
            for (int j = r; j >= l && cnt; j--)
                if(!d[j]) cnt--, ans++, d[j] = 1;
        }
    }
    printf("%d\n", ans);
    return 0;
}

最適化

データ構造の最適化を考えてみましょう。

我々は3つのことを見つける必要があります。

  • 問い合わせ\([L、R] \ ) 桁間隔
  • \(X \)位置\(1 + \)
  • バックからフロント、フロントへの現在の位置よりも低く見つけるために\(0 \)の位置。
  1. 最初の二つは「間隔が加算され、単調な編集」フェンウィックツリーの代表的です。$ O(nlog_250000)$

  2. 第3の動作は、互いに素セット最適化することがあります。なぜそれがその時の複雑さを確保することができますか?各セグメントについて、列挙するために、一度だけ最大で\(1 \) 即ち、その時間の開始)、各列挙の後に列挙されます(\ 0)\すなわち位置、\(D [i]は= 0 \)し、その後にそれを回す\(1 \) その後へのアクセス権を持っていません。そして、の合計(\ 50,000)\値、複雑であるので、\(O(50000log_n)\)

\(33ms \)

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 50001;
int n, d[N], c[N], f[N];
struct Seg{
    int a, b, c;
    bool operator < (const Seg &x) const {
        return b < x.b;
    }
}e[N];
// 树状数组
int inline ask(int x) {
    int res = 0;
    for (; x; x -= x & -x) res += c[x];
    return res;
}

void inline add(int x) {
    for (; x < N; x += x & -x) c[x]++;
}
// 并茶集:find(x) 表示找到 <= x 中最大的一个是 0 的数
int find(int x) {
    return x == f[x] ? x : f[x] = find(f[x]);
}
int main() {
    scanf("%d", &n);
    for (int i = 0; i < N; i++) f[i] = i;
    for (int i = 1; i <= n; i++) 
        scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].c);
    sort(e + 1, e + 1 + n);
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int l = e[i].a, r = e[i].b, cnt = e[i].c;
        // 取 [l, r] 选了多少个数
        cnt -= ask(r) - ask(l - 1);
        if(cnt > 0) {
            for (int j = r; j >= l && cnt; ) {
                // d[j] == 1 的情况每条线段至多出现一次
                if(!d[j]) {
                    cnt--, ans++, d[j] = 1;
                    // j 被标记成 1 了,要指向 find(j - 1)
                    f[j] = j - 1;
                    // 维护树状数组
                    add(j);
                };
                if(find(j) != j) j = f[j];
                else j--;
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

おすすめ

転載: www.cnblogs.com/dmoransky/p/11923718.html