Hướng dẫn giải của Bedao Mini Contest 09 - SOR


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.

Tác giả: bedao

Trước hết ta nên nhận xét được một dãy sẽ thỏa khi nào.

Xét 2 bit ~a~ và ~b~, ta có bảng toán tử sau :

a b a + b a or b
0 0 0 0
0 1 1 1
1 0 1 1
1 1 2 1

Nhìn bảng, ta thấy được phép cộng và phép or chỉ có kết quả khác nhau khi ~a = b = 1~.

Vậy ta kết luận : Một dãy thỏa khi và chỉ khi, với mỗi vị trí bit, có nhiều nhất ~1~ bit ~1~ xuất hiện trong dãy.

Tuy nhiên nếu ta cứ chạy và cập nhật ngây thơ thì sẽ dễ dàng chạy quá thời gian, nên chúng ta sẽ có thêm nhận xét sau.

Nhận xét quan trọng : Vì ~a_i > 0~, nên ~a_i~ có ít nhất một bit ~1~.

Hệ quả : Dãy thỏa không bao giờ dài quá ~log_2(a_i)~.

Từ đó ta có thuật toán đơn giản sau :

  • Gọi ~ans_u~ là dãy thỏa dài nhất bắt đầu từ vị trí ~u~. Dễ thấy đáp án là ~max(ans_i)~. Ta sẽ dùng set hoặc heap (hoặc bất kỳ ctdl nào bạn muốn) để lưu lại các số trong mảng ~ans~ để tiện lấy đáp án.
  • Ban đầu precalculate mảng ans trong ~O(n * log(a_i))~.
  • Với truy vấn cập nhật ở vị trí ~u~, ta chỉ cập nhật lại ~ans_i~ với ~u - 31 \le i \le u~. Độ phức tạp : ~O(log^2(a_i))~ hoặc ~O(log(a_i))~ nếu dùng 2 con trỏ.
  • Vì sau mỗi truy vấn có tối đa ~log(a_i)~ giá trị ~ans_i~ được cập nhật lại nên ta sẽ cập nhật lại đáp án trong : ~O(log(n) \times log(a_i))~

Vậy độ phức tạp của thuật toán là : ~O(q \times log(a_i) \times (log(a_i) + log(n)))~

Code mẫu

/*#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("avx,avx2,fma")*/

#include <bits/stdc++.h>

#define for1(i,a,b) for (int i = a; i <= b; i++)
#define for2(i,a,b) for (int i = a; i >= b; i--)
#define int long long

#define sz(a) (int)a.size()
#define pii pair<int,int>
#define pb push_back

/*
__builtin_popcountll(x) : Number of 1-bit
__builtin_ctzll(x) : Number of trailing 0
*/

const long double PI = 3.1415926535897932384626433832795;
const int INF = 1000000000000000000;
const int MOD = 1000000007;
const int MOD2 = 1000000009;
const long double EPS = 1e-6;

using namespace std;

const int N = 1e5 + 5, LG = 29;
int n, q, a[N], val[N], cnt[LG + 5];

int get() {
    for2(i,LG,1) if (cnt[i]) return i;
    return 0;
}

signed main() {

    ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);

    // freopen("cf.inp", "r", stdin);
    // freopen("cf.out", "w", stdout);

    cin >> n >> q;
    for1(i,1,n) cin >> a[i];

    int r = 1,cur = 0;
    for1(l,1,n) {
        while (r <= n && (cur ^ a[r]) == (cur | a[r])) {
            cur ^= a[r];
            r++;
        }
        val[l] = r - l;
        cnt[val[l]]++;
        cur ^= a[l]; 
    }

    while (q--) {
        int u, v;
        cin >> u >> v;
        a[u] = v;
        int r = max(1ll, u - LG + 1), cur = 0;
        for1(l,max(1ll, u - LG + 1),u) {
            while (r <= n && (cur ^ a[r]) == (cur | a[r])) {
                cur ^= a[r];
                r++;
            }
            cnt[val[l]]--;
            val[l] = r - l;
            cnt[val[l]]++;
            cur ^= a[l]; 
        }

        cout << get() << "\n";
    }

}

Bình luận

Hãy đọc nội quy trước khi bình luận.


Không có bình luận tại thời điểm này.