煉石計劃 NOIP 模擬賽 #20

你说得太对辣發表於2024-11-16

A.

\(kx + (\sum_{i=1}^{k} a_i - 1) \times y = k(x - y) + y \times \sum_{i=1}^{k} a_i\)

\((a_1 - 1) * 1 + (a_2 - 1) * (a_1 - 1) * 1 + (a_3 - 1) * (a_2 - 1) * (a_1 - 1) * 1\)

$ \prod_{i=1}^{k} a_i > N$

兩數和相等時乘積最大,因此 \(a\) 陣列中任意兩個數的差的絕對值小於等於1(一定是形如 \(p, p + 1\))。

設有 \(t\)\(p\)

\(p^t \times (p + 1) ^ {k-t} > N\)

\(k(x - y) + y \times (t \times p + (k - t) \times (p + 1)) = k(x-y) + y \times (k \times p + k - t)\)

變成了滿足一式的情況下要求二式最小。

列舉 \(k\)\(\log N\),可以再列舉一個 \(t\),再對 \(p\) 二分。

時間複雜度 \(\log ^4 N\)

程式碼
#include<bits/stdc++.h>
#define int __int128
using namespace std;

const int N = 1e6 + 7;
const int M = 61;
const int inf = 1e20;
void write(int x)
{
    if(x<0)
        putchar('-'),x=-x;
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
    return;
}
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9')
        x=x*10+ch-'0',ch=getchar();
    return x*f;
}
signed main() {
    freopen("dice.in", "r", stdin);
    freopen("dice.out", "w", stdout);
    int N, x, y;
    N = read(), x = read(), y = read();
    int ans = inf;
    for(int k = 1; k <= M; k ++) {
        int l = 0, r = inf;
        for(int t = 1; t <= k; t ++) {
            int l = 0, r = inf;
            int res;
            while(l <= r) {
                int mid = l + r >> 1;
                int flag;
                int now = 1;
                int ck = 1;
                for(int i = 1; i <= t; i ++) {
                    now = now * mid;
                    if(now > N) {
                        ck = 0;
                        break;
                    }
                }
                if(!ck) flag = 1;
                if(ck) {
                    for(int i = 1; i <= k - t; i ++) {
                        now = now * (mid + 1);
                        if(now > N) {
                            ck = 0;
                            break;
                        }
                    }
                }
                
                if(!ck) flag = 1;

                if(ck && now > N) flag = 1;
                else if(ck) flag = 0;
                if(flag) {
                    res = mid;
                    r = mid - 1;
                }
                else l = mid + 1;
            }
            // cout << k * (x - y) + y * (k * res + k - t) << endl;
            ans = min(ans, (k * (x - y) + y * (k * res + k - t)));
        }
    }
    write(ans);
}

B.

好題。

\(L_i (j)\) 表示序列中等於 \(i\) 的第 \(j\) 個出現的位置。我們要找最小的 \(k\),使得 \(L_k(j) - L_k(j-1) > x\),記 \(f_i\) 表示 \(L_i(j) - L_i(j-1)\) 的最大值, \(G(i)\) 表示 \(\max_{j=1}^{i}f(j)\),要求的就是最小的 \(k\) 使得 \(G(k) > x\)

對於交換 \(a_p, a_{p+1}\),找到 \(x, y\) 滿足 \(L_{a[p]}(x) = p, L_{a_[p+1]}(y) = p + 1\)\(L_{a[p]}[x] ++,L_{a[p + 1]}[y] --\)。然後用線段樹維護 \(G\),每次查詢二分即可,時間複雜度 \(O(q\log^2n)\)

程式碼
#include<bits/stdc++.h>
using namespace std;

const int N = 5e5 + 10;

int a[N];

struct tree {
    int l, r;
    int mxx;
}T[N << 2];

void build(int l, int r, int rt = 1) {
    T[rt].l = l, T[rt].r = r;
    if(l == r) {
        return;
    }
    int mid = l + r >> 1;
    build(l, mid, rt << 1);
    build(mid + 1, r, rt << 1 | 1);
    T[rt].mxx = max(T[rt << 1].mxx, T[rt << 1 | 1].mxx);
}
void modify(int pos, int x, int rt = 1) {
    if(T[rt].l == T[rt].r) {
        T[rt].mxx = x;
        return;
    }
    int mid = T[rt].l + T[rt].r >> 1;
    if(pos <= mid) modify(pos, x, rt << 1);
    else modify(pos, x, rt << 1 | 1);
    T[rt].mxx = max(T[rt << 1].mxx, T[rt << 1 | 1].mxx);
}
int query(int l, int r, int rt = 1) {
    if(l <= T[rt].l && r >= T[rt].r) {
        return T[rt].mxx;
    }
    int mid = T[rt].l + T[rt].r >> 1;
    int ans = -1;
    if(l <= mid) ans = max(ans, query(l, r, rt<< 1));
    if(r > mid) ans = max(ans, query(l, r, rt << 1 | 1));
    
    return ans;
}
set<int> L[N];
multiset<int> dis[N];


void ins(int x) {
    
    auto it = L[a[x]].lower_bound(x);
    int r = *it, l = *(--it);
    L[a[x]].insert(x);
    dis[a[x]].erase(dis[a[x]].lower_bound(r - l - 1));
    dis[a[x]].insert(r - x - 1);
    dis[a[x]].insert(x - l - 1);
    modify(a[x] + 1, *dis[a[x]].rbegin());
}
void del(int x) {
    L[a[x]].erase(x);
    auto it = L[a[x]].lower_bound(x);
    int r = *it, l = *(--it);
    dis[a[x]].insert(r - l - 1);
    dis[a[x]].erase(dis[a[x]].lower_bound(r - x - 1));
    dis[a[x]].erase(dis[a[x]].lower_bound(x - l - 1));
    modify(a[x] + 1, *dis[a[x]].rbegin());
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    freopen("star.in", "r", stdin);
    freopen("star.out", "w", stdout);
    int n, q;
    cin >> n >> q;
    build(1, n + 1);
    for(int i = 0; i < n; i ++) {
        L[i].insert(0), L[i].insert(n + 1);
        dis[i].insert(n + 1);
        modify(i + 1, n + 1);
    }
    for(int i = 1; i <= n; i ++) {
        cin >> a[i];
        ins(i);
    }
    while(q --) {
        int x, y;
        cin >> x >> y;
        if(x == 1) {
            del(y);
            del(y + 1);
            swap(a[y], a[y + 1]);
            ins(y);
            ins(y + 1);
        }
        else {
            int l = 1, r = n;
            int ans = n + 1;
            while(l <= r) {
                int mid = l + r >> 1;
                if(query(1, mid) >= y) {
                    ans = mid;
                    r = mid - 1;
                }
                else l = mid + 1;
            }
            cout << ans - 1 << endl;
        }
    }
}

相關文章