Codeforces Round #765 (Div. 2)ABCD题解

A. Ancient Civilization

题意:给定n个数,要求找出一个数y,使得 \(\sum d(x_i, y)\) 最小,其中 \(d(a, b)\) 表示a,b两数的二进制表达式中,不同的位数个数

解法:对每位考虑,如果该位1多,就设为1,否则设为0

#include <bits/stdc++.h>
#define endl '\n'
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL,LL> PLL;
const int INF = 0x3f3f3f3f, N = 1e5 + 10;
const int MOD = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
inline int lowbit(int x) {return x & (-x);}

inline void solve() {
    int n, l; cin >> n >> l;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    int res = 0;
    for (int i = l - 1; ~i; i -- ) {
        int cnt = 0;
        for (int j = 1; j <= n; j ++ ) {
            if (a[j] >> i & 1) cnt ++;
        }
        if (cnt > n - cnt) res |= 1 << i;
    }
    cout << res << endl;
}
int main() {
#ifdef DEBUG
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    auto now = clock();
#endif
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cout << fixed << setprecision(2);
    int T; cin >> T;
    while (T -- )
        solve();
#ifdef DEBUG
    cout << "============================" << endl;
    cout << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
    return 0;
}

B. Elementary Particles

题意:给定一个长度为n的序列,要求找到两个长度为k的连续区间a,b,使得存在一个数i,满足 \(a[i] = b[i]\) 表示的是分别是a,b中的第i个,求k的最大值

解法:贪心

假如我们找到了两个相同的数,设分别在p1, p2,要求框出这段范围,那么这个范围的最大值就是 \(p1 - p2 + n\),所以要让 \(p1 - p2\) 最大,也就是两个要尽量挨得近,存一下相同数的下标, 遍历即可。

#include <bits/stdc++.h>
#define endl '\n'
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL,LL> PLL;
const int INF = 0x3f3f3f3f, N = 2e5 + 10;
const int MOD = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
inline int lowbit(int x) {return x & (-x);}

vector<int> pos[N];

inline void solve() {
    int n; cin >> n;

    vector<int> a(n + 1), st(N);

    for (int i = 1; i <= n; i ++ ) {
        cin >> a[i];
        st[a[i]] ++;
        pos[a[i]].push_back(i);
    }
    int res = -1;
    for (int i = 1; i <= 150000; i ++ ) {
        if (st[i] >= 2) {
            int mn = -INF;
            for (int j = 0; j < st[i] - 1; j ++ ) mn = max(mn, pos[i][j] - pos[i][j + 1]);
            res = max(res, mn + n);
        }
        pos[i].clear();
    }
    cout << res << endl;
}
int main() {
#ifdef DEBUG
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    auto now = clock();
#endif
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cout << fixed << setprecision(2);
    int T; cin >> T;
    while (T -- )
        solve();
#ifdef DEBUG
    cout << "============================" << endl;
    cout << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
    return 0;
}

C. Road Optimization

题意:从A走到B的路上有n个限速牌,表示你接下来走一公里至少需要的时间,AB间距为 \(l\),现在你最多可以拆掉 \(k\) 个限速牌,使得到达B的时间尽量少,问最少时间。

解法:DP

状态设计:\(dp[i][j]\) 表示到 \(i\) 点时拆了 \(j\) 个限速牌花费的最少时间,其中 \(i\) 点不拆。

状态转移:\(dp[i][j] = min_{p = 0}^{j} (dp[i - p - 1][j - p] + (d[i] - d[i - p - 1]) * a[i - p - 1])\)

看作01背包问题就好理解了

#include <bits/stdc++.h>
#define endl '\n'
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL,LL> PLL;
const int INF = 0x3f3f3f3f, N = 510;
const int MOD = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
inline int lowbit(int x) {return x & (-x);}

LL dp[N][N];

inline void solve() {
    int n, l, k; cin >> n >> l >> k;
    vector<int> d(n + 2), a(n + 2);
    for (int i = 1; i <= n; i ++ ) cin >> d[i];
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    d[n + 1] = l;
    memset(dp, 0x3f, sizeof dp);
    dp[1][0] = 0;
    for (int i = 2; i <= n + 1; i ++ ) {
        for (int j = 0; j <= k; j ++ ) {
            dp[i][j] = dp[i - 1][j] + (d[i] - d[i - 1]) * a[i - 1];
            for (int p = 1; p <= j; p ++ ) {
                if (i - p - 1 <= 0) break;
                dp[i][j] = min(dp[i][j], dp[i - p - 1][j - p] + (d[i] - d[i - p - 1]) * a[i - p - 1]);
            }
        }
    }
    LL res = INF;
    for (int i = 0; i <= k; i ++ ) res = min(res, dp[n + 1][i]);
    cout << res << endl;
}
int main() {
#ifdef DEBUG
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    auto now = clock();
#endif
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cout << fixed << setprecision(2);
//    int T; cin >> T;
//    while (T -- )
        solve();
#ifdef DEBUG
    cout << "============================" << endl;
    cout << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
    return 0;
}

D. Binary Spiders

题意:给定n个数和一个数k,要求在n个数中找出一个集合,使得该集合为原数组的子集,且其中的任意两数的异或和至少为k,问最大子集是什么,给出任意一解即可。

解法:Trie

首先要明白,这是位运算,所以我们对每一位分别进行考虑。

我们假设k的最高位1的位置在x(二进制表达中的右边开始数)

在二进制表达式中,我们把每个数的第x及以下的位数抹掉,剩下的数相同的我们叫做同一类书。

首先很显然,不同类的任意两数的异或和一定大于k

其次,同一类的两数中,我们最多可以选2个

假如我们选了两个同一类的数,如果要使得他们的异或和大于等于k,那么必然两数的第x位是不同的

知道了以上后,可以用抽屉原理来解释,如果选了三个的话,那么必定存在任意两个同一类的数,使得第x位相同,从而异或和小于k

那么就简单了,首先对所有数分类,这一步用哈希下即可

然后对同一类中,利用trie找找看是否能有两个数的异或和大于等于k即可,有就选上,没有就随便选一个

#include <bits/stdc++.h>
#define endl '\n'
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL,LL> PLL;
const int INF = 0x3f3f3f3f, N = 3e5 + 10, M = N * 30;
const int MOD = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
inline int lowbit(int x) {return x & (-x);}

int tr[M][2], num;
int ed[M];
int n, k;
vector<int> ans;

void init() {
    for (int i = 0; i <= num; i ++ ) {
        tr[i][0] = tr[i][1] = ed[i] = 0;
    }
    num = 0;
}

void Insert(int x, int idx) {
    int p = 0;
    for (int i = 30; ~i; i -- ) {
        int c = x >> i & 1;
        if (!tr[p][c]) tr[p][c] = ++ num;
        p = tr[p][c];
    }
    ed[p] = idx;
}

int Find(int x) {
    int p = 0, sum = 0;
    for (int i = 30; ~i; i -- ) {
        int c = x >> i & 1;
        if (tr[p][c ^ 1]) {
            p = tr[p][c ^ 1];
            sum |= (c ^ 1) * (1 << i);
        } else {
            p = tr[p][c];
            sum |= c * (1 << i);
        }
    }
    sum = sum ^ x;
    if (sum >= k) return ed[p];
    else return -1;
}

inline void solve() {
    cin >> n >> k;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    int p = 0;
    for (int i = 30; ~i; i -- ) {
        if (k >> i & 1) {
            p = i;
            break;
        }
    }
    int base = (1 << 30) - (1 << (p + 1));
    if (k == 0) {
        cout << n << endl;
        for (int i = 1; i <= n; i ++ ) cout << i << ' ';
        cout << endl;
        return ;
    }
    unordered_map<int, vector<PII>> f;
    for (int i = 1; i <= n; i ++ ) {
        int now = a[i] & base;
        f[now].emplace_back(a[i], i);
    }
    for (auto ite : f) {
        init();
        for (auto i2 : ite.second) Insert(i2.first, i2.second);
        bool is = false;
        for (auto i2 : ite.second) {
            int idx = i2.second;
            int tmp = Find(a[idx]);
            if (tmp != -1) {
                ans.push_back(tmp);
                ans.push_back(idx);
                is = true;
                break;
            }
        }
        if (!is) ans.push_back(ite.second[0].second);
    }
    if (ans.size() <= 1) cout << -1 << endl;
    else {
        cout << ans.size() << endl;
        for (auto ite : ans) cout << ite << ' ';
        cout << endl;
    }
}
int main() {
#ifdef DEBUG
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    auto now = clock();
#endif
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cout << fixed << setprecision(2);
//    int T; cin >> T;
//    while (T -- )
        solve();
#ifdef DEBUG
    cout << "============================" << endl;
    cout << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
    return 0;
}
posted @ 2022-01-13 15:29  Time_Limit_Exceeded  阅读(138)  评论(2编辑  收藏  举报