比赛链接:
https://vjudge.net/contest/513349
A - ^&^
题意:
找到使 (a xor c) & (b xor c) 最小数,若这个数有多个,输出最小的。
思路:
如果 a 和 b 的某位不同,那么最后 c 的这位不论什么,都不影响结果,所以这一位是 0。
如果相同,且都为 0,要让 c 的这位等于 0 才是最小的。
如果相同且都为 1,那只有为 1 的时候这位才是最小的。
所以答案就是 a & b,特判一下等于 0 的情况。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
LL a, b;
cin >> a >> b;
if ((a & b) == 0) cout << "1\n";
else cout << (a & b) << "\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T = 1;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
B - array
题意:
给定一个长为 \(n\) 的序列,每个元素不相同,进行 \(m\) 次操作:
(1, \(pos\)):表示让 \(a_pos = a_pos + 10000000\)
(2, \(r\), \(k\)):表示询问不小于 \(k\),且不等于 \(a_i(1 <= i <= r)\) 的值。
思路:
根据元素值建立权值线段树,树节点的值是元素的下标。
查询就是找 \((k, n)\) 中比 \(r\) 大的最小值。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5 + 10, INF = 1e9;
int n, m, w[N];
struct Segt{
struct node{
LL l, r, pos, mx;
}tr[N << 2];
void pushup(LL u){
tr[u].mx = max(tr[u << 1].mx, tr[u << 1 | 1].mx);
}
void build(LL u, LL l, LL r){
if (l == r){
tr[u] = {l, r, w[r], w[r]};
return;
}
tr[u] = {l, r};
LL mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void update(LL u, LL p){
if (tr[u].l == tr[u].r){
tr[u].pos = tr[u].mx = INF;
}
else {
if (p <= tr[u << 1].r) update(u << 1, p);
else update(u << 1 | 1, p);
pushup(u);
}
}
LL query(LL u, LL l, LL r, LL x){
if (tr[u].l == tr[u].r) return tr[u].l;
int ans = INF;
if (l <= tr[u << 1].r && x < tr[u << 1].mx) ans = query(u << 1, l, r, x); //优先找左边小的
if (ans != INF) return ans;
if (r >= tr[u << 1 | 1].l && x < tr[u << 1 | 1].mx) ans = query(u << 1 | 1, l, r, x);
return ans;
}
}segt;
void solve(){
cin >> n >> m;
vector < pair<int, int> > a(n + 1);
for (int i = 1; i <= n; i ++ ){
cin >> a[i].first;
a[i].second = i;
}
auto b = a;
sort(b.begin() + 1, b.end());
for (int i = 1; i <= n; i ++ )
w[i] = b[i].second;
w[ ++ n] = INF; //设立右端点
segt.build(1, 1, n);
int ans = 0;
for (int i = 1; i <= m; i ++ ){
int op;
cin >> op;
if (op == 1){
int x;
cin >> x;
x = x ^ ans;
segt.update(1, a[x].first);
}
else{
int r, k;
cin >> r >> k;
r = r ^ ans, k = k ^ ans;
ans = segt.query(1, k, n, r);
cout << ans << "\n";
}
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T = 1;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
通过主席树维护区间的最小值,然后用 \(set\) 存删除的值,二者取小即是答案。
先将所有位置的值设为对应下标,初始输入的时候将所有出现的值设为 \(INF\),每次删除一个数,将这个数加入 \(set\),查询就输出区间最小值和 \(set\) 中的最小值即可。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5 + 10, INF = 1e9;
struct ChairTree{
int idx = 0, root[N];
struct node{
int l, r, mn;
}tr[N << 5];
void pushup(int u){
tr[u].mn = min(tr[tr[u].l].mn, tr[tr[u].r].mn);
}
void build(int &u, int l, int r){
u = ++ idx;
if (l == r){
tr[u].mn = l;
return;
}
int mid = l + r >> 1;
build(tr[u].l, l, mid);
build(tr[u].r, mid + 1, r);
pushup(u);
}
void update(int &v, int u, int l, int r, int pos){
v = ++ idx;
tr[v] = tr[u];
if (l == r){
tr[v].mn = INF;
return;
}
int mid = l + r >> 1;
if (pos <= mid) update(tr[v].l, tr[u].l, l, mid, pos);
else update(tr[v].r, tr[u].r, mid + 1, r, pos);
pushup(v);
}
int query(int u, int l, int r, int L, int R){
if (l >= L && r <= R) return tr[u].mn;
int mid = (l + r) >> 1, ans = INF;
if (L <= mid) ans = min(ans, query(tr[u].l, l, mid, L, R));
if (R > mid) ans = min(ans, query(tr[u].r, mid + 1, r, L, R));
return ans;
}
}ct;
void solve(){
int n, m;
cin >> n >> m;
ct.idx = 0;
ct.build(ct.root[0], 1, n + 1);
vector <int> a(n + 1);
for (int i = 1; i <= n; i ++ ){
cin >> a[i];
ct.update(ct.root[i], ct.root[i - 1], 1, n + 1, a[i]);
}
int ans = 0;
set <int> s;
for (int i = 1; i <= m; i ++ ){
int op;
cin >> op;
if (op == 1){
int x;
cin >> x;
x ^= ans;
s.insert(a[x]);
}
else{
int r, k;
cin >> r >> k;
r ^= ans, k ^= ans;
ans = ct.query(ct.root[r], 1, n + 1, k, n + 1);
auto it = s.lower_bound(k);
if (it != s.end()){
ans = min(ans, *it);
}
cout << ans << "\n";
}
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
D - path
题意:
给定 \(n\) 个点 \(m\) 条边的有向带权图,\(q\) 次询问,每次问第 \(k\) 小的路径长是多少。
思路:
暴力做法就是将所有边塞入队列中依次弹出,第几次出来的边就是第几短的边,但是这会 \(MLE\)。考虑从最短的边开始枚举。
所有边按长度排序之后,将每个点的最小出边塞入优先队列里面。每次弹出头部的边,将比当前出边大一点的出边及加上尾部节点的最小出边再塞入队列中即可。
红色的边是 \(u\) 节点中比 \((u, v)\) 长的中最小的出边,蓝色的边是 \(v\) 节点最小的出边。
对于 \((u, v)\) 这条边而言,除了 \((u, v)\) 外最小的边只有这两种可能。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
struct node{
int len, u, id;
bool operator < (const node &x) const{
return len > x.len;
}
};
void solve(){
int n, m, q;
cin >> n >> m >> q;
vector < vector < pair<int, int> > > g(n + 1);
for (int i = 0; i < m; i ++ ){
int u, v, w;
cin >> u >> v >> w;
g[u].push_back({w, v});
}
for (int i = 1; i <= n; i ++ )
sort(g[i].begin(), g[i].end());
int mx = 0;
vector <int> query(q);
for (int i = 0; i < q; i ++ ){
cin >> query[i];
mx = max(mx, query[i]);
}
vector <int> ans(mx + 1);
priority_queue <node> que;
for (int i = 1; i <= n; i ++ ){
if (g[i].size()){
que.push({g[i][0].first, i, 0});
}
}
int cnt = 0;
while(que.size()){
node t = que.top();
que.pop();
int len = t.len, u = t.u, id = t.id;
ans[ ++ cnt] = len;
if (cnt == mx) break;
if (id < (int)(g[u].size() - 1)){
que.push({len - g[u][id].first + g[u][id + 1].first, u, id + 1});
}
int v = g[u][id].second;
if (g[v].size()){
que.push({len + g[v][0].first, v, 0});
}
}
for (int i = 0; i < q; i ++ )
cout << ans[query[i]] << "\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T = 1;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
F - Shuffle Card
题意:
给定一个长为 \(n\) 的排列,每次选择一个数将它放到开头,进行 \(m\) 次操作,问最后的序列是什么。
思路:
离线处理操作,通过 \(map\) 反向走一遍 \(m\) 次操作,然后将数字压入答案数组中,最后正向判断排列中哪些数字没有被选中,按照顺序压入答案数组中。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int n, m;
cin >> n >> m;
vector <int> a(n), b(m);
for (int i = 0; i < n; i ++ )
cin >> a[i];
for (int i = 0; i < m; i ++ )
cin >> b[i];
vector <int> ans;
map <int, int> mp;
for (int i = m - 1; i >= 0; i -- ){
if (mp.count(b[i])) continue;
ans.push_back(b[i]);
mp[b[i]] = i + 1;
}
for (int i = 0; i < n; i ++ ){
if (!mp.count(a[i])){
ans.push_back(a[i]);
}
}
for (auto x : ans)
cout << x << " ";
return 0;
}
G - Windows Of CCPC
题意:
输出 \(2^n * 2^n\) 的指定矩阵。具体看样例。
思路:
简单签到,一个递归。
将 C 看成 1,P 看成 0。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
char a[1100][1100];
void dfs(int x, int y, int len, int v){
if (len == 0){
a[x][y] = (v == 1 ? 'C' : 'P');
return;
}
dfs(x, y, len / 2, v);
dfs(x, y + len, len / 2, v);
dfs(x + len, y, len / 2, 1 - v);
dfs(x + len, y + len, len / 2, v);
}
void solve(){
int n;
cin >> n;
int k = pow(2, n);
dfs(1, 1, k / 2, 1);
for (int i = 1; i <= k; i ++ ){
for (int j = 1; j <= k; j ++ ){
cout << a[i][j];
}
cout << "\n";
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T = 1;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
H - Fishing Master
题意:
有 \(n\) 条鱼在鱼塘里面,将鱼钓上来需要花 \(k\) 的时间,将第 \(i\) 条鱼煮熟需要花 \(a_i\) 的时间,不能同时煮两条鱼或者钓两条鱼,问最少花多少时间可以把鱼全煮熟。
思路:
当煮一条鱼的时间大于钓鱼的时间,在肯定优先钓这条鱼,然后利用煮鱼的时间去钓其它鱼。
设钓鱼用了 \(k\) 的时间,煮鱼用了 \(t\) 的时间。
\(t - k\) 的时间要怎么办,继续等鱼煮熟还是再钓一条。当手中还有鱼的时候,肯定等当前这个鱼熟了之后再去钓鱼,但是没鱼的时候只能去钓鱼才是最优的。
根据贪心的策略,要让鱼熟的时间和钓鱼的时间差的小的时候去钓鱼才节约时间。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
int n, k;
cin >> n >> k;
vector <int> a(n);
LL ans = k, cnt = 0;
for (int i = 0; i < n; i ++ ){
cin >> a[i];
ans += a[i];
cnt += a[i] / k;
a[i] %= k;
}
sort(a.rbegin(), a.rend()); //对剩余的时间进行排序
for (int i = 0; i < n - cnt - 1; i ++ ) //减 1 是因为第一条一定要钓上来
ans = ans - a[i] + k;
cout << ans << "\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T = 1;
cin >> T;
while(T -- ){
solve();
}
return 0;
}