2019 HDUマルチ学校の研修最初のB-操作

トピックへのリンク:http://acm.hdu.edu.cn/showproblem.php?pid=6579

問題の意味は:2つの動作モードがあり、長さnの配列は整数ある
; 0 LR [、R&LT L]図面の一部から選択され、は、それらの排他的または最大、最大出力という
。追加のX 1、Xはシーケンスの終了、及びN = N + 1ましょう。
必須件名が、lastansは答える0は最後の操作、初期ゼロを表します。
各動作のための0、MOD Lは=(L個の排他的論理和lastans)ように 1 + N、R =(R XOR lastans)MOD N + 1、 L> R 2つのスイッチング素子であれば。
各アクション1について、X = XのXOR lastansをしましょう。

この質問を読んだ後、XORと最大値を参照して、ゲームは、最初に考えたのは直線的な拠点です。XORベースの直線範囲と最大値は、気持ちが急に直線のベースラインツリー、それに移動することはできません。この操作を合併しているリニアグループを考え、うまく処理されていませんか?だから私は、次のようにコードがある(間違った解決策を)書いてみます:

//这是一份错解
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000005;
int n, q, a[MAXN];
struct L_B
{
    int a[32];
    void init()
    {
        memset(a, 0, sizeof(a));
    }
    bool insert(int val)
    {
        for (int i = 30; i >= 0; --i)
        {
            if (val & (1 << i))
            {
                if (!a[i])
                {
                    a[i] = val;
                    break;
                }
                else
                    val ^= a[i];
            }
        }
        return val > 0;
    }
    int query_max()
    {
        int ret = 0;
        for (int i = 30; i >= 0; --i)
        {
            if ((ret ^ a[i]) > ret)
                ret ^= a[i];
        }
        return ret;
    }
    L_B merge(L_B m)
    {
        L_B ret;
        for (int i = 0; i < 31; i++)
            ret.a[i] = a[i];
        for (int i = 0; i < 31; i++)
        {
            for (int j = i; j >= 0; j--)
            {
                if (m.a[i] & (1 << j))
                {
                    if (ret.a[j])
                        m.a[i] ^= ret.a[j];
                    else
                    {
                        ret.a[j] = m.a[i];
                        break;
                    }
                }
            }
        }
        return ret;
    }
} tr[MAXN * 4];
void build(int l, int r, int root)
{
    if (l == r)
    {
        tr[root].init();
        tr[root].insert(a[l]);
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, root << 1);
    build(mid + 1, r, root << 1 | 1);
    tr[root] = tr[root << 1].merge(tr[root << 1 | 1]);
}
void add(int l, int r, int pos, int root, int ans)
{
    if (l == pos && r == pos)
    {
        tr[root].init();
        tr[root].insert(ans);
        return;
    }
    int mid = (l + r) >> 1;
    if (pos > mid)
        add(mid + 1, r, pos, root << 1 | 1, ans);
    else
        add(l, mid, pos, root << 1, ans);
    tr[root] = tr[root << 1].merge(tr[root << 1 | 1]);
}
L_B query(int L, int R, int l, int r, int root)
{
    if (L <= l && r <= R)
        return tr[root];
    int mid = (l + r) >> 1;
    if (L <= mid && mid < R)
        return query(L, R, l, mid, root << 1).merge(query(L, R, mid + 1, r, root << 1 | 1));
    if (L <= mid)
        return query(L, R, l, mid, root << 1);
    if (R > mid)
        return query(L, R, mid + 1, r, root << 1 | 1);
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        memset(a, 0, sizeof(a));
        scanf("%d %d", &n, &q);
        for (int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        int len = n + q;
        build(1, len, 1);
        int lastans = 0, op;
        L_B ans;
        for (int i = 1; i <= q; ++i)
        {
            scanf("%d", &op);
            if (op == 0)
            {
                int l, r;
                scanf("%d %d", &l, &r);
                l = (l ^ lastans) % n + 1;
                r = (r ^ lastans) % n + 1;
                if (l > r)
                    swap(l, r);
                ans.init();
                ans = query(l, r, 1, len, 1);
                lastans = ans.query_max();
                printf("%d\n", lastans);
            }
            else if (op == 1)
            {
                int x;
                scanf("%d", &x);
                x = x ^ lastans;
                n++;
                add(1, len, n, 1, x);
            }
        }
    }
    return 0;
}

何の問題を確認しないようにコードを書いた後、TLE、外に支払うことにしてみてください。残業は、場所を見つけることができません最適化する方法を見つけることか、TLE、あなたのコードをダブルチェックし、再び突然、時間の複雑さを忘れるの操作はO(900)であり、木のラインのように動作しない線形のグループを一緒に見出さO(1)です。計算の複雑さが間違っている時間が経過した後、私は私の考えが正しいことを疑うようになりました。(ゲームは私が.JPGを持っていませんでした)

ソリューション:
暴力の実践は、データ・ベース区間線形構造を維持するが、確かにいなかったことができます。(クライ)
貪欲維持接頭線形の塩基配列(三角形)、各グループの線形、できるだけデジタル高の位置の右側に表示され、それは、同時に記録されるように、新たな番号が挿入されると言うことです出現位置位置番号に対応する、と場所を挿入したとき、見つけることができれば、低い数値に向かってプッシュの元の位置に元の位置より右側にさらに新たなデジタル数、。
ハイからロートラバーサルに、最大値を選択するときはビット区間の左端の右の数字は尋問に表示され、答えは大きくすることができるならば、それは排他的論理和への答えです。
すべてのリニアグループの場合、それは確かにそれの右側に表示されます高い位置に基づいて、排他的またはデジタルリニアをしていた(またはその位置に挿入されます)ので、実際は明らかに正確です。

説明:
実際には、問題の解決策は、その後、我々は、リニアグループを構築するためのすべての位置の配列を決定することができ、場所ごとに番号が最高レベルへの貢献され、リニアベースのための貪欲なアイデアを使用することです、線形配列の各々は、各グループの寄与の位置を記録しながら、各位置におけるリニアグループ番号は、右のように取られます。そのベース上に位置する前に新しい番号を挿入するときに、前の線状直鎖基が同じであり、その後、一方の唯一最高寄与の新しいデジタル挿入に貢献することができる新しいデジタルの場所を見つけ、それは、デジタルで置換されていてもよいですしたがって、置換いくつかのグループの背後に更新リニアは、更新番号を進めます。
限り、このビットリニアイルがハイからローに横断するように、最大値を選択した場合、右のビット間隔の左端の番号が課題に表示され、答えは大きくすることができるならば、それは排他的論理和への答えです。

コードは以下の通りであります:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000005;
int a[MAXN], L_B[MAXN][31], pos[MAXN][31];
void add(int p, int x)
{
    int k = p;
    for (int i = 30; i >= 0; --i)
    {
        L_B[p][i] = L_B[p - 1][i];
        pos[p][i] = pos[p - 1][i];
    }
    for (int i = 30; i >= 0; --i)
    {
        if (x >> i)
        {
            if (!L_B[p][i])
            {
                L_B[p][i] = x;
                pos[p][i] = k;
                break;
            }
            else
            {
                if (k > pos[p][i])
                {
                    swap(k, pos[p][i]);
                    swap(x, L_B[p][i]);
                }
                x ^= L_B[p][i];
            }
        }
    }
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        int n, q, op, ans = 0;
        scanf("%d %d", &n, &q);
        for (int i = 1; i <= n; ++i)
        {
            scanf("%d", &a[i]);
            add(i, a[i]);
        }
        while (q--)
        {
            scanf("%d", &op);
            if (op == 1)
            {
                scanf("%d", &a[++n]);
                a[n] ^= ans;
                add(n, a[n]);
            }
            else
            {
                int l, r;
                scanf("%d %d", &l, &r);
                l = (l ^ ans) % n + 1;
                r = (r ^ ans) % n + 1;
                if (l > r)
                    swap(l, r);
                ans = 0;
                for (int j = 30; j >= 0; --j)
                    if ((ans ^ L_B[r][j]) > ans && pos[r][j] >= l)
                        ans ^= L_B[r][j];
                printf("%d\n", ans);
            }
        }
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 30; j >= 0; --j)
            {
                L_B[i][j] = 0;
                pos[i][j] = 0;
            }
        }
    }
    return 0;
}

おすすめ

転載: www.cnblogs.com/red-leaves/p/11269487.html