Codeforces Round #765 (Div. 2)ABCD题解
题意:给定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;
}
题意:给定一个长度为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;
}
题意:从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;
}
题意:给定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;
}