The 2022 ICPC Asia Xi'an Regional Contest

发布时间 2023-09-30 10:44:15作者: PHarr

C. Clone Ranran

最优解一定是先复制,在做题。最多只需要复制大约 30 次,直接枚举即可

#include <bits/stdc++.h>

using namespace std;

#define int long long

int a , b, c;

void solve(){
    cin >> a >> b >> c;
    int res = LLONG_MAX;
    for( int i = 0 , t = 1 ; i < 31 ; i ++ , t *= 2 )
        res = min(res , a * i + (c + t  -1) / t * b);
    cout << res << "\n";
}

int32_t main(){
    int T;
    for( cin >> T ; T ; T -- )
        solve();
}

E. Find Maximum

所谓的\(f\)函数实际上就是把\(x\)转为三进制,三进制的长度加每一位数字之和。

所以最大值,一定长度和\(r\)相同,然后我们枚举\(r\)的每一位,把当前位减一,后面的位全部变成二。这样取最大值即可,同时要保证操作后的数字大于\(l\)

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int inf = 1e18;

using vi = vector<int>;

unordered_map<int, int> g;

int f(int x) {
    if (g[x] != 0) return g[x];
    if (x % 3 == 0) return g[x] = f(x / 3) + 1;
    else return g[x] = f(x - x % 3) + x % 3;
}

void solve() {
    int l, r;
    cin >> l >> r;

    vector<int> a;

    for (int x = r; x; x /= 3) a.push_back(x % 3);
    std::reverse(a.begin(), a.end());
    int res = a.size() + accumulate(a.begin(), a.end(), 0ll);

    vector<int> b;
    for (int x = l; x; x /= 3) b.push_back(x % 3);
    while( b.size() < a.size() ) b.push_back(0);
    std::reverse(b.begin(), b.end());

    for (int i = 0; i < a.size(); i++) {
        auto c = a;
        if( c[i] == 0 ) continue;
        c[i] --;
        for( int j = i + 1 ; j < a.size() ; j ++ ) c[j] = 2;
        if( c < b ) continue;
        res = max( res , (int)c.size() + accumulate( c.begin(), c.end() , 0ll ) - (c.front() == 0) );
    }
    cout << res << "\n";
    return;
}


int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int t;
    g[0] = 1;
    for (cin >> t; t; t--)
        solve();

    return 0;
}

F. Hotel

根据性别数量,可以判断出住房的方案,在多个方案中取最优解即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main(){
    ios::sync_with_stdio(false) , cin.tie(nullptr);
    int n , c1 , c2 , res = 0;
    cin >> n >> c1 >> c2;
    string s;
    for( int i = 1 , cnt ; i <= n ; i ++ ){
        set<char> sex;
        cin >> s;
        for( auto c : s ) sex.insert( c );
        cnt = min( 3 * c1 , 3 * c2 );
        if( sex.size() < 3 ){
            cnt = min( { cnt , c1 + c2 , c2 + c2 });
        }
        res += cnt;
    }
    cout << res << "\n";
    return 0;
}

G. Perfect Word

字符串哈希,然后枚举子串,判断子串是否存在即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define mp make_pair
using hashv = pair<int, int>;

const hashv mod = mp(1e9 + 7, 998244353);
const hashv base = mp(13331, 23333);

hashv operator+(hashv a, hashv b) {
    int c1 = a.first + b.first, c2 = a.second + b.second;
    if (c1 >= mod.first) c1 -= mod.first;
    if (c2 >= mod.second) c2 -= mod.second;
    return mp(c1, c2);
}

hashv operator-(hashv a, hashv b) {
    int c1 = a.first - b.first, c2 = a.second - b.second;
    if (c1 < 0) c1 += mod.first;
    if (c2 < 0) c2 += mod.second;
    return mp(c1, c2);
}

hashv operator*(hashv a, hashv b) {
    return mp(a.first * b.first % mod.first, a.second * b.second % mod.second);
}

const int N = 1e5 + 5;
vector<hashv> p(N);

void hashStr(const string &s, vector<hashv> &v) {
    v.resize(s.size() + 1);
    for (int i = 1; i <= s.size(); i++)
        v[i] = v[i - 1] * base + mp(s[i - 1], s[i - 1]);
    return;
}

hashv getHash(int l, int r, const vector<hashv> &v) {
    if (l > r) return mp(0, 0);
    return v[r] - v[l - 1] * p[r - l + 1];
}

void init() {
    p[0] = mp(1, 1);
    for (int i = 1; i < N; i++)
        p[i] = p[i - 1] * base;
}


int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    init();
    int n;
    cin >> n;
    vector<string> s(n);
    vector<vector<hashv>> hv(n);
    for (auto &i: s) cin >> i;
    set<hashv> vis;
    for (int i = 0; i < n; i++)
        hashStr(s[i], hv[i]), vis.insert(hv[i].back());
    int res = 0;
    for (int i = 0, len, f; i < n; i++) {
        len = s[i].size(), f = 1;
        if (len <= res) continue;
        for (int l = 1; f and l <= len; l++)
            for (int r = l; f and r <= len; r++)
                if (vis.count(getHash(l, r, hv[i])) == 0) f = 0;
        if (f) res = len;
    }
    cout << res << "\n";
    return 0;
}

J. Strange Sum

\(i\)\(n\),即找最大的两个值

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main(){
    ios::sync_with_stdio(false) , cin.tie(nullptr);
    int n;
    cin >> n;
    vector<int> a(n);
    for( auto &i : a ) cin >> i;
    sort( a.begin() , a.end() , greater<int>() );
    cout << max( { 0ll , a[0] , a[0] + a[1] } );
}

L. Tree

题目相当于用最少的链和反链数量覆盖一棵有根树。

如果一个点被反链覆盖,但它的儿子没有被反链覆盖,因为必然存在一条链经过它的儿子,所以必然存在一条链经过它,将覆盖该点的反链调整为覆盖它的儿子,答案不会变劣。

因此,每添加一条反链,都相当于剥去当前树上的所有叶子。设 \(f_i\) 表示 \(i\) 的子树内最深的叶子与 \(i\) 的距离,这里距离定义为简单路径上的点数,容易证明 \(i\) 会被第 \(f_i\) 条反链覆盖。

假设我们不再添加反链,则每个叶子至少对应一条链,且每条链可以覆盖至少一个叶子,所以需要的链的数量为叶子数量。

如果我们用了 \(j - 1\) 条反链,那么 \(f_i = j\) 的节点 \(i\) 会裸露成叶子。因此,设 \(cnt_j\) 表示 \(f_i = j\)\(i\) 的数量,则 \(\min\limits_{j = 1} ^ n cnt_j + j - 1\) 即为所求。时间复杂度 \(\mathcal{O}(n)\)

#include <bits/stdc++.h>

using namespace std;

using vi = vector<int>;


void solve() {
    int n;
    cin >> n;
    vector<vi> e(n + 1);
    vi dis(n + 1, 0);
    for (int i = 2, x; i <= n; i++)
        cin >> x, e[x].push_back(i);

    auto dfs = [e, &dis](auto &&self, int x) -> void {
        for (auto y: e[x])
            self(self, y), dis[x] = max(dis[x], dis[y] + 1);
        return;
    };
    dfs(dfs, 1);

    vi cnt(dis[1] + 1);
    for (int i = 1; i <= n; i++)
        cnt[dis[i]]++;
    int res = INT_MAX;
    for (int i = 0; i <= dis[1]; i++)
        res = min(res, i + cnt[i]);
    cout << res << "\n";
}

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int TC;
    for (cin >> TC; TC; TC--)
        solve();
    return 0;
}