2022 ICPC 香港区域赛 FL

F. Sum of Numbers

高精度,\(dp\),暴搜。

要使得 \(n\) 位分成 \(k+1\) 段后相加结果数值最小,那么应该每段都尽可能的平分成 \(\frac n{k+1}\) 位,然后剩下的 \(n\%(k+1)\) 也平分,但这里可能会存在某段被多分两位,某段需要去掉一位,比如:

1
11 3
73992931311

1776

这里就是按照 \(2,2,3,4\) 的分法,\(4\) 就比 \(\frac{11}4\) 多两位,是个需要处理的细节。

减一位也有,这里提供一组数据:

1
100 6
1324842515955461999676716711641298225484496862171152246515638182113621698497576949417794359275794291

576285078539320

最多分成 \(7\) 段,\(\text{dfs}\) 暴搜每段,相邻段的长度差值在 \(-1,0,+1\),大概 \(3^7\) 次,中间应该有大量不合法的状态剪剪枝就过了,注意不要写成三进制枚举的写法,这样复杂度会多一个 \(k\),过不了。

点击查看代码
#pragma GCC optimize (3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
string operator+(string a,string b) {
    if(a=="INF" || b=="INF") return "INF";
    reverse(a.begin(),a.end());
    reverse(b.begin(),b.end());
    if(a.size()<b.size()) swap(a,b);
    for(int i=0;i<b.size();i++) a[i]+=b[i]-'0';
    for(int i=0;i<a.size();i++) {
        if(a[i]>'9') {
            a[i]-=10;
            if(i+1==a.size()) a.push_back('1');
            else a[i+1]++;
        }
    }
    reverse(a.begin(),a.end());
    return a;
}
bool operator<(string a,string b) {
    if(a.size()!=b.size()) return a.size()<b.size();
    for(int i=0;i<a.size();i++) {
        if(a[i]!=b[i]) return a[i]<b[i  ];
    }
    return false;
}

void solve() {
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    vector<int> lt(k + 1);
    vector<int> c(k + 1);
    string ans = "";
    function<void(int idx)> dfs = [&](int idx) {
        if (idx > k) {
            int sum = 0;
            for (int i = 0; i <= k; ++i) {
                lt[i] = c[i];
                if (i)lt[i] += lt[i - 1];
                sum += lt[i];
            }
            if ((n - sum) % (k + 1) == 0) {
                int base = (n - sum) / (k + 1);
                if (base>0) {
                    string res = "";
                    int len = 0;
                    int vs=1;
                    for (auto x: lt) {
                        if(x+base<=0) {
                            vs=0;
                            break;
                        }
                    }
                    if(vs) {
                        for (auto x: lt) {
                            if (x + base <= 0) {
                                vs = 0;
                                break;
                            }
                            res = res + s.substr(len, x + base);
                            len += base + x;
                        }
                        if (vs and ans.empty() or res < ans) {
                            swap(ans, res);
                        }
                    }
                }
            }
            return;
        }
        for (int i = -1; i <= 1; ++i) {
            c[idx] = i;
            dfs(idx + 1);
        }

    };
    dfs(1);
    cout << ans << endl;
}

signed main() {
//	freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t=1;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

考虑 \(dp[i][j]\) 表示前 \(j\) 位分成 \(i\) 段的最小值,那么转移的时候要保证每段在 \([\frac{n}{k+1}-1,\frac{n}{k+1}+2]\) 的范围内去取 \(dp[i][j]=\min(dp[i][j],dp[i-1][j-x])\)\(x\) 在即合法段的长度。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

struct BigInteger {
    static const int BASE = 100000000;
    static const int WIDTH = 8;
    std::vector<int> s;
    bool sign;

    BigInteger(long long num = 0) {
        *this = num;
    }
    BigInteger(const std::string& str) {
        *this = str;
    }

    BigInteger operator = (long long num) {
        s.clear();
        sign = (num < 0);
        if (num < 0) num = -num;
        do {
            s.push_back(num % BASE);
            num /= BASE;
        } while (num > 0);
        return *this;
    }

    BigInteger operator = (const std::string& str) {
        s.clear();
        std::string numStr = str;
        sign = false;
        if (numStr[0] == '-') {
            sign = true;
            numStr = numStr.substr(1);
        }

        int x, len = (numStr.length() - 1) / WIDTH + 1;
        for (int i = 0; i < len; i++) {
            int end = numStr.length() - i * WIDTH;
            int start = std::max(0, end - WIDTH);
            sscanf(numStr.substr(start, end - start).c_str(), "%d", &x);
            s.push_back(x);
        }
        return *this;
    }

    void normalize() {
        while (s.size() > 1 && s.back() == 0) s.pop_back();
        if (s.size() == 1 && s[0] == 0) sign = false;
    }

    bool operator < (const BigInteger& b) const {
        if (sign != b.sign) return sign;
        if (s.size() != b.s.size())
            return sign ? s.size() > b.s.size() : s.size() < b.s.size();
        for (int i = s.size() - 1; i >= 0; i--)
            if (s[i] != b.s[i])
                return sign ? s[i] > b.s[i] : s[i] < b.s[i];
        return false;
    }

    bool operator > (const BigInteger& b) const {
        return b < *this;
    }
    bool operator <= (const BigInteger& b) const {
        return !(b < *this);
    }
    bool operator >= (const BigInteger& b) const {
        return !(*this < b);
    }
    bool operator != (const BigInteger& b) const {
        return b < *this || *this < b;
    }
    bool operator == (const BigInteger& b) const {
        return !(b < *this) && !(*this < b);
    }

    BigInteger operator + (const BigInteger& b) const {
        if (sign == b.sign) {
            BigInteger c;
            c.sign = sign;
            c.s.clear();
            for (int i = 0, g = 0; ; i++) {
                if (g == 0 && i >= s.size() && i >= b.s.size()) break;
                int x = g;
                if (i < s.size()) x += s[i];
                if (i < b.s.size()) x += b.s[i];
                c.s.push_back(x % BASE);
                g = x / BASE;
            }
            return c;
        } else {
            if (sign) {
                BigInteger temp = *this;
                temp.sign = false;
                return b - temp;
            } else {
                BigInteger temp = b;
                temp.sign = false;
                return *this - temp;
            }
        }
    }

    BigInteger operator - (const BigInteger& b) const {
        if (sign != b.sign) {
            BigInteger temp = b;
            temp.sign = !b.sign;
            return *this + temp;
        }

        if (*this == b) return BigInteger(0);

        BigInteger c;
        if ((!sign && *this > b) || (sign && *this < b)) {
            c = *this;
            for (int i = 0, g = 0; i < c.s.size() || i < b.s.size(); i++) {
                int x = c.s[i] - g;
                if (i < b.s.size()) x -= b.s[i];
                if (x < 0) {
                    g = 1;
                    x += BASE;
                } else {
                    g = 0;
                }
                if (i < c.s.size()) c.s[i] = x;
                else c.s.push_back(x);
            }
            c.normalize();
            c.sign = sign;
        } else {
            c = b - *this;
            c.sign = !sign;
        }
        return c;
    }

    BigInteger abs() const {
        BigInteger result = *this;
        result.sign = false;
        return result;
    }

    friend std::ostream& operator << (std::ostream& out, const BigInteger& x) {
        if (x.sign && !(x.s.size() == 1 && x.s[0] == 0)) out << '-';
        out << x.s.back();
        for (int i = x.s.size() - 2; i >= 0; i--) {
            char buf[20];
            sprintf(buf, "%08d", x.s[i]);
            out << buf;
        }
        return out;
    }

    friend std::istream& operator >> (std::istream& in, BigInteger& x) {
        std::string s;
        if (!(in >> s)) return in;
        x = s;
        return in;
    }
};

void solve() {

    int n, k;
    std::cin >> n >> k;

    std::string s;
    std::cin >> s;

    k += 1;
    int down = std::max(1, n / k - 1), up = std::min(n / k + 2, n - k + 1);
    s = " " + s;

    std::vector<BigInteger> dp(n + 1, -1);
    dp[0] = 0;

    for(int i = 1; i <= k; i += 1) {
        std::vector<BigInteger> ndp(n + 1, -1);
        for(int j = 1; j <= n; j += 1) {
            if(down * i <= j && j <= up * i && down * (k - i) <= (n - j) && (n - j) <= up * (k - i)) {
                for(int x = down; x <= up; x += 1) {
                    if(x > j || dp[j - x] == -1) continue;
                    std::string tmp = s.substr(j - x + 1, x);
                    if(ndp[j] == -1){
                        ndp[j] = dp[j - x] + BigInteger(tmp);
                    }else{
                        ndp[j] = std::min(ndp[j], dp[j - x] + BigInteger(tmp));
                    }
                }
            }
        }
        dp = std::move(ndp);
    }

    std::cout << dp[n] << "\n";

}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t;
    std::cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

L. Permutation Compression

数据结构。

首先要让 \(a\) 删除数后得到 \(b\),显然要满足 \(b\)\(a\) 的子集。

因为每次删除的都是最大值,考虑从大往小枚举可删除的数,找左右两边比当前数大的位置,假设 \(l\)\(r\) 代表左右两边比当前数大的位置,那么 \([l+1,r-1]\) 这个区间内都以 \(i\) 为最大值,那么只要存在 \(L_i\) 小于等于 \(r-l-1\) 就可以把 \(i\) 删掉;把 \(i\) 删掉后,为了防止 \(i\) 对后续的影响,需要标记一下,在后续有 \(j\) 找两边最大值时越过了 \(i\) 的位置,需要减掉它的影响,所以对 \(j\) 来说,删除 \(j\) 的区间长度为 \(r-l-1-cnt\)\(cnt\) 代表 \([l+1,r-1]\) 区间内有多少个比 \(j\) 大的数被删掉了,可以用线段树维护这个过程。

最后比较一下每个数可删除的区间长度是不是都能找到一个 \(L_i\) 对应即可,可以将两者排序后一一比较即可。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

template<class Info>
struct SegmentTree {
    int n;
    std::vector<Info> info;
    SegmentTree() : n(0) {}
    SegmentTree(int n_, Info v_ = Info()) {
        init(n_, v_);
    }
    template<class T>
    SegmentTree(std::vector<T> init_) {
        init(init_);
    }
    void init(int n_, Info v_ = Info()) {
        init(std::vector(n_, v_));
    }
    template<class T>
    void init(std::vector<T> init_) {
        n = init_.size();
        info.assign(4 << std::__lg(n), Info());
        std::function<void(int, int, int)> build = [&](int p, int l, int r) {
            if (r - l == 1) {
                info[p] = init_[l];
                return;
            }
            int m = (l + r) / 2;
            build(2 * p, l, m);
            build(2 * p + 1, m, r);
            pull(p);
        };
        build(1, 0, n);
    }
    void pull(int p) {
        info[p] = info[2 * p] + info[2 * p + 1];
    }
    void modify(int p, int l, int r, int x, const Info &v) {
        if (r - l == 1) {
            info[p] = v;
            return;
        }
        int m = (l + r) / 2;
        if (x < m) {
            modify(2 * p, l, m, x, v);
        } else {
            modify(2 * p + 1, m, r, x, v);
        }
        pull(p);
    }
    void modify(int p, const Info &v) {
        modify(1, 0, n, p, v);
    }
    Info rangeQuery(int p, int l, int r, int x, int y) {
        if (l >= y || r <= x) {
            return Info();
        }
        if (l >= x && r <= y) {
            return info[p];
        }
        int m = (l + r) / 2;
        return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y);
    }
    Info rangeQuery(int l, int r) {
        return rangeQuery(1, 0, n, l, r);
    }
    template<class F>
    int findFirst(int p, int l, int r, int x, int y, F &&pred) {
        if (l >= y || r <= x) {
            return -1;
        }
        if (l >= x && r <= y && !pred(info[p])) {
            return -1;
        }
        if (r - l == 1) {
            return l;
        }
        int m = (l + r) / 2;
        int res = findFirst(2 * p, l, m, x, y, pred);
        if (res == -1) {
            res = findFirst(2 * p + 1, m, r, x, y, pred);
        }
        return res;
    }
    template<class F>
    int findFirst(int l, int r, F &&pred) {
        return findFirst(1, 0, n, l, r, pred);
    }
    template<class F>
    int findLast(int p, int l, int r, int x, int y, F &&pred) {
        if (l >= y || r <= x) {
            return -1;
        }
        if (l >= x && r <= y && !pred(info[p])) {
            return -1;
        }
        if (r - l == 1) {
            return l;
        }
        int m = (l + r) / 2;
        int res = findLast(2 * p + 1, m, r, x, y, pred);
        if (res == -1) {
            res = findLast(2 * p, l, m, x, y, pred);
        }
        return res;
    }
    template<class F>
    int findLast(int l, int r, F &&pred) {
        return findLast(1, 0, n, l, r, pred);
    }
};

constexpr int inf = 1E9;

struct Info {
    int max = -inf, cnt = 0;
};

Info operator+(const Info &l, const Info &r) {
    return {std::max(l.max, r.max), l.cnt + r.cnt};
}

void solve() {

    int n, m, k;
    std::cin >> n >> m >> k;

    SegmentTree<Info> seg(n);

    std::vector<int> a(n), b(m), c(k), vis(n), pos(n);
    for(int i = 0; i < n; i += 1) {
        std::cin >> a[i];
        a[i] -= 1;
        pos[a[i]] = i;
        seg.modify(i, {a[i], 0});
    }
    for(int i = 0; i < m; i += 1) {
        std::cin >> b[i];
        b[i] -= 1;
        vis[b[i]] = 1;
    }
    for(int i = 0; i < k; i += 1) {
        std::cin >> c[i];
    }

    int idx = 0;
    for(int i = 0; i < n; i += 1) {
        if(idx < m && a[i] == b[idx]) {
            idx += 1;
        }
    }
    if(idx != m) {
        std::cout << "NO\n";
        return;
    }

    std::vector<int> sz;
    for(int i = n - 1; i >= 0; i -= 1) {
        if(vis[i]) continue;

        int l = -1,r = n;
        auto x = seg.findLast(0, pos[i], [&](auto p) {
            return p.max > i;
        });
        l = x;
        x = seg.findFirst(pos[i] + 1, n, [&](auto p) {
            return p.max > i;
        });
        if(x != -1) {
            r = x;
        }
        l += 1, r -= 1;
        sz.push_back(r - l + 1 - seg.rangeQuery(l, r + 1).cnt);
        seg.modify(pos[i], {0, 1});
    }

    if(c.size() < sz.size()) {
        std::cout << "NO\n";
        return;
    }

    sort(c.begin(), c.end());
    sort(sz.begin(), sz.end());

    for(int i = 0; i < sz.size(); i += 1) {
        if(c[i] > sz[i]) {
            std::cout << "NO\n";
            return;
        }
    }

    std::cout << "YES\n";

}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t;
    std::cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}
posted @ 2025-10-15 14:53  Ke_scholar  阅读(10)  评论(0)    收藏  举报