Hướng dẫn giải của Bedao Grand Contest 10 - NUMBER


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

Subtask 1

Dễ dàng nhận thấy, số lớn thứ ~k~ trong dãy có giá trị ~a_1 \cdot k~.

Subtask 2

Ta gọi ~minValue~ là giá trị nhỏ nhất của mảng ~a~.

Ta có mảng ~f~ với ~f_i~ ~(0 \leq i < minValue)~ là số có giá trị nhỏ nhất trong dãy ~b~ chia ~minValue~ dư ~i~. Dựng một đồ thị gồm ~minValue~ đỉnh đánh số lần lượt từ ~0, 1, ..., minValue - 1~, nối cạnh ~u~ ~\rightarrow~ ~(u + a_i)~ ~\%~ ~minValue~ trọng số ~a_i~. Vì ~a_1 + a_2 + ...+ a_n \leq 10^6~ ~\Rightarrow~ ~minValue \leq \frac{10^6}{n}~, mỗi đỉnh có tối đa ~n-1~ cạnh nên đồ thị của ta có không quá ~\frac{10^6}{n} \cdot (n-1) \leq 10^6~ cạnh. Giả sử dãy ~b~ có chứa cả giá trị ~0~, ta đặt ~f_0 = 0~ và dùng thuật toán Dijkstra để tính mảng ~f~.

Độ phức tạp: ~O(n \cdot minValue \cdot log2(minValue))~

Từ mảng ~f~, ta suy ra số nhỏ thứ nhì chia ~minValue~ dư ~i~ là ~f_i + minValue~, số nhỏ thứ ba chia minValue dư ~i~ là ~f_i + minValue \cdot 2~, ..., số nhỏ thứ ~x~ chia minValue dư ~i~ là ~f_i + minValue \cdot (x-1)~.

Để tìm số lớn thứ ~k~ của dãy ~b~, ta dùng thuật toán chặt nhị phân các giá trị từ ~1~ đến ~10^{15}~). Để kiểm tra xem đáp án có ~\leq mid~ hay không, ta đếm số lượng giá trị của mảng ~b \leq mid~ bằng cách xét các giá trị ~f_i \leq mid~, lấy tổng ~cnt = \frac{mid}{minValue} - \frac{f_i}{minValue} + (mid \% minValue \geq f_i \% minValue)~. Nếu ~cnt \geq (k+1)~ thì đáp án sẽ ~\leq mid~ (~+1~ do không tính giá trị ~0~).

Độ phức tạp: ~O(q \cdot log2(10^{15}) \cdot minValue)~.

Subtask 3

Ta tính mảng ~f~ tương tự Subtask 2. Vì ~k \leq 5000~ nên ta có thể chuẩn bị trước các giá trị của mảng ~b~ bằng cách dùng cấu trúc dữ liệu priority_queue. Ban đầu ta cho hết tất cả các giá trị ~f_i~ vào trong priority_queue. Tại mỗi lượt, gọi ~cur~ là giá trị nhỏ nhất trong priority_queue hiện tại, ta lấy ~cur~ ra, cho giá trị ~cur~ vào mảng ~b~ và thêm giá trị ~cur + minValue~ vào lại priority_queue.

ĐPT phần này là ~O(5000 \cdot log2(minValue)~.

Subtask 4

Ta tính mảng ~f~ tương tự Subtask 2. Ta sort các giá trị của mảng ~f~ tăng dần và sort các truy vấn tăng dần theo giá trị ~k~. Sử dụng kĩ thuật sweep line để tính đáp án. Ta chia các giá trị ~i~ là dư khi chia cho ~minValue~ vào các nhóm có chỉ số ~\frac{f_i}{minValue}~.

Gọi ~sz_i~ là số lượng giá trị dư thuộc các nhóm có chỉ số ~\leq i~, ~g_i~ là số lượng số thuộc mảng ~b~ có giá trị ~< i \cdot minValue~. Ta có ~g_i~ = ~g_j~ + ~sz_j \cdot (j - i)~ với ~j~ là chỉ số nhóm lớn nhất ~< i~. Ta xác định được chỉ số ~x~ max trong số các nhóm mà ~g_x \leq k~, đặt ~tmp = k - g_x~. Đáp án của câu hỏi số lớn thứ ~k~ trong dãy ~b~ là ~(\frac{tmp}{sz_x} + x) \cdot minValue)~ + giá trị dư lớn thứ ~tmp \% sz_x + 1~ trong số các giá trị dư thuộc nhóm có chỉ số ~\leq x~.

Ta dùng cấu trúc dữ liệu Ordered Set để thuận tiện cho việc tính toán.

Độ phức tạp: ~O((minValue + q) \cdot log2(minValue))~.

Code mẫu

//TrungNotChung
#include <bits/stdc++.h>
#define pii pair<int , int>
#define fi first
#define se second
#define BIT(x,i) (1&((x)>>(i)))
#define MASK(x)  (1LL<<(x))
#define CNT(x) __builtin_popcountll(x)
#define task "tnc"
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp> // Including tree_order_statistics_node_update
using namespace std;
using namespace __gnu_pbds;

using ordered_set = tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update>;

const int N = (int)3e5+228;
const int M = (int)1e6+228;
const int INF = (int)1e9;
const long long INFLL = (long long)1e18;

int n, q, a[N], b[N];
long long f[M], res[N];

void solve()
{
    cin >> n >> q;
    for(int i=1; i<=n; ++i) cin >> a[i];
    memset(f, 0x3f, sizeof(f));
    int mi = a[1];
    for(int i=1; i<=n; ++i) mi = min(mi, a[i]);
    priority_queue<pair<long long, int>, vector<pair<long long, int> >, greater<pair<long long, int> > > pq;
    f[0] = 0;
    pq.push(make_pair(0, 0));
    while(pq.size())
    {
        long long value = pq.top().fi;
        int x = pq.top().se;
        pq.pop();
        if(f[x] != value) continue;
        for(int i=1; i<=n; ++i)
        {
            int y = (x + a[i]) % mi;
            if(f[y] > value + a[i])
            {
                f[y] = value + a[i];
                pq.push(make_pair(f[y], y));
            }
        }
    }
    // cout << q << '\n';
    vector<pair<long long, int> > g;
    for(int i=0; i<mi; ++i) if(f[i] < INFLL) g.push_back(make_pair(f[i], i));
    sort(g.begin(), g.end());

    vector<pii> qu(q);
    for(int i=0; i<q; ++i)
    {
        cin >> qu[i].fi;
        qu[i].se = i+1;
    }
    sort(qu.begin(), qu.end());

    int lst = 0, sz = 0;
    long long cnt = 0;
    int cur = 0;
    ordered_set ods;

    for(int i=0; i<=(int)g.size();)
    {
        // cout << lst << " " << sz << " ";
        long long nextCnt = (i == (int)g.size() ? INFLL : cnt + 1LL * (g[i].fi / mi - lst) * sz);
        // cout << nextCnt << ":\n";
        while(cur < (int)qu.size() && qu[cur].fi < nextCnt)
        {
            // cout << qu[cur].fi << " " << qu[cur].se << " " << '\n';
            int k = qu[cur].fi - cnt;
            int id = qu[cur].se;
            res[id] = 1LL * (k / sz + lst) * mi + *ods.find_by_order(k % sz);
            ++cur;
        }
        if(i == (int)g.size()) break;
        int sta = i;
        while(i != (int)g.size() && g[i].fi / mi == g[sta].fi / mi)
        {
            ods.insert(g[i].se);
            ++sz;
            cnt = nextCnt;
            if(cnt > INF) break;
            lst = g[i].fi / mi;
            ++i;
        }
        if(cnt > INF) break;
    }

    for(int i=1; i<=q; ++i) cout << res[i] << '\n';

}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    //freopen(task".inp" , "r" , stdin);
    //freopen(task".out" , "w" , stdout);
    solve();
    return 0;
}

Bình luận

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



  • 0
    nguyenhuunhan  đã bình luận lúc 1, Tháng 11, 2022, 13:41

    "Dễ dàng" là dễ thế nào thế ạ? :<