牛客小白月赛29全题解
牛客小白月赛29全题解
A.进攻
题目链接:https://ac.nowcoder.com/acm/contest/8564/A
思路:
- 将基地按防御值为第一关键字,价值为第二关键字从小到大排序。将战机按破坏力从小到大排序。
- 由于战机能破坏任意防御值比其破坏力小的基地,所以选择破坏防御值比其破坏力小的价值最大的基地即可
- 有2可以导出,先预处理出每个基地的价值的前缀最大值,这个最大值就是可以破坏该基地的战机所能获得的最大价值
- 从大到小扫遍历机和基地,将可以摧毁的且价值大于0的价值累加即为答案
注意:1、价值可能为负数;2、在遍历的时候,在战机遍历完之前,基地可能已经遍历完了
由于第二条,卡了近一个小时,wa了无数发。。。
AC代码:
#include <iostream>
#include <algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6+10;
int n, m, a[N];
PII dv[N];
int main()
{
cin >> n >> m;
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
for(int i = 0; i < m; i++) scanf("%d", &dv[i].x);
for(int i = 0; i < m; i++) scanf("%d", &dv[i].y);
sort(a, a + n);
sort(dv, dv + m);
int mv = -1e3 - 10;
for(int i = 0; i < m; i++)
{
mv = max(mv, dv[i].y);
dv[i].y = mv;
}
int ans = 0;
for(int i = n - 1, j = m - 1; ~i && ~j;)
{
if(a[i] > dv[j].x)
{
if(dv[j].y > 0) ans += dv[j].y;
else break;
i--;
}
else j--;
}
cout << ans << endl;
return 0;
}
B.二进制
题目链接:https://ac.nowcoder.com/acm/contest/8564/B
可以确定,一定可以通过三步与、或、异或得到最后的结果。
观察01与、或、异或的值:
与; 或: 异或:
0 & 1 = 0 0 | 1 = 1 0 ^ 1 = 1
1 & 1 = 1 1 | 1 = 1 1 ^ 1 = 0
0 & 0 = 0 0 | 0 = 0 0 ^ 0 = 0
1 & 0 = 0 1 | 0 = 1 1 ^ 0 = 1
可以发现
- 0/1 & 1值不变、0/1 | 0值不变、0/1 ^ 0值不变
- 开始为0,结果为0;开始为1, 结果为0的情况是&0
- 开始为0,结果为1;开始为1, 结果为1的情况是|1
- 开始为0,结果为1;开始为1, 结果为0的情况是^0
所以我们可以以0和220-1为初始值,然后经过所有操作后得结果,一位一位比较前后值的变化,依次推断出该位是&0还是|1还是^1
AC代码:
#include <iostream>
#include <bitset>
using namespace std;
int n;
bitset<20> ans1((1 << 20) - 1), ans2(0), ans3(0);
int main()
{
cin >> n;
int x1 = 0, x2 = (1 << 20) - 1;
while(n--)
{
int op, a;
scanf("%d%d", &op, &a);
if(op == 1) x1 = x1 & a, x2 = x2 & a;
else if(op == 2) x1 = x1 | a, x2 = x2 | a;
else x1 = x1 ^ a, x2 = x2 ^ a;
}
cout << 3 << endl;
for(int i = 0, j = 0; i < 20; i++, j++)
{
if(((x1 >> i) & 1) == 0 && ((x2 >> i) & 1) == 0)
ans1[j] = 0;
else if(((x1 >> i) & 1) == 1 && ((x2 >> i) & 1) == 1) ans2[j] = 1;
else if(((x1 >> i) & 1) == 1 && ((x2 >> i) & 1) == 0) ans3[j] = 1;
}
cout << 1 << ' ' << ans1.to_ulong() << endl;
cout << 2 << ' ' << ans2.to_ulong() << endl;
cout << 3 << ' ' << ans3.to_ulong() << endl;
return 0;
}
C.积木
题目链接:https://ac.nowcoder.com/acm/contest/8564/C
构造题
参考题解:积木_欢迎您的光顾awa-CSDN博客
AC代码:
/*
采用构造方法:
00 11
00 11
交替出现
*/
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
if(n & 1)
{
puts("-1");
return 0;
}
for(int h = 1; h <= n; h++)
{
for(int k = ((h & 1) ? 0 : 1), i = 0; i < n; i += 2, k = (k + 1) % 2)
{
for(int x = k, j = 0; j < n; j += 2)
{
printf("%d %d ", x, x);
x = (x + 1) % 2;
}
putchar('\n');
for(int x = k, j = 0; j < n; j += 2)
{
printf("%d %d ", x, x);
x = (x + 1) % 2;
}
putchar('\n');
}
}
return 0;
}
D.种树
题目链接:https://ac.nowcoder.com/acm/contest/8564/D
根据题目要求可得,需要剪枝的次数\(m = (n - 1) / 2\),所以用大剪刀的次数为\(ma = (m + 1) / 2\)
分析可得:如果我们在最开始就用大剪刀的话,后面用小剪刀会把最大值替换掉,也就是说先用大剪刀会被后面的小剪刀抵消掉。
由此可的:从深度为ma的那一层点开始,到根节点全部用大剪刀,深度大于ma的全部用小剪刀。根据题意遍历完所有节点,同时更新。最后根节点的生命力即为结果。
AC代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5e5+10;
int n, m, ma, l, r, v, root;
bool st[N];
struct Node
{
int l, r, v;
bool yz;
}tr[N];
void dfs(int root, int h)
{
int ls = tr[root].l, rs = tr[root].r;
if(ls) dfs(ls, h + 1);
if(rs) dfs(rs, h + 1);
if(tr[ls].yz && tr[rs].yz)
{
if(h >= ma) tr[root].v = min(tr[ls].v, tr[rs].v);
else tr[root].v = max(tr[ls].v, tr[rs].v);
}
tr[root].l = tr[root].r = 0, tr[root].yz = true;
}
int main()
{
cin >> n;
m = n / 2;
ma = (m + 1) / 2;
for(int i = 1; i <= n; i++)
{
scanf("%d%d", &l, &r);
tr[i].l = l, tr[i].r = r, tr[i].yz = !(l && r);
st[l] = st[r] = true;
}
for(int i = 1; i <= n; i++)
{
scanf("%d", &v);
tr[i].v = v;
}
//找根节点
for(int i = 1; i <= n; i++)
if(!st[i])
{
root = i;
break;
}
dfs(root, 0);
cout << tr[root].v << endl;
return 0;
}
E.考试
题目链接:https://ac.nowcoder.com/acm/contest/8564/E
首先遍历,计算出两人答案相同的题目数量t
- 当\(t(相同数量)<= n- k(答案正确的数量)\)时,相同的全对,不同的自己正确,ans = t + k
- 当\(t(相同数量)> n- k(答案正确的数量)\),相同部分全对,剩余的相同部分全错,ans = n - (t - (n - k))
AC代码:
#include <iostream>
using namespace std;
const int N = 1010;
int n, k;
int a[N], t;
int main()
{
cin >> n >> k;
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
for(int i = 0; i < n; i++)
{
int x;
scanf("%d", &x);
if(x == a[i]) t++;
}
if(t <= n - k) cout << t + k << endl;
else cout << n - (t - (n - k)) << endl;
return 0;
}
F.项链
题目链接:https://ac.nowcoder.com/acm/contest/8564/F
写一个循环双链表即可,翻转就是l[]和r[]
交换
AC代码:
#include <iostream>
using namespace std;
const int N = 2e5+10;
int n, m, op, x, y;
int e[N], l[N], r[N], idx, p[N], t[N];
void insert_r(int a, int x)
{
e[idx] = x, p[x] = idx;
l[idx] = a, r[idx] = r[a];
l[r[a]] = idx, r[a] = idx++;
}
void insert_l(int a, int x)
{
e[idx] = x, p[x] = idx;
r[idx] = a, l[idx] = l[a];
r[l[a]] = idx, l[a] = idx++;
}
void remove(int a)
{
l[r[a]] = l[a];
r[l[a]] = r[a];
}
void init(int n)
{
r[0] = 1, l[0] = 1;
r[1] = 0, l[1] = 0;
idx = 2;
for(int i = 1; i <= n; i++) insert_r(idx - 1, i);
}
int main()
{
cin >> n >> m;
init(n);
while(m--)
{
scanf("%d", &op);
if(op == 1 || op == 2) scanf("%d%d", &x, &y);
if(op == 1)
{
remove(p[x]);
insert_r(p[y], x);
}
else if(op == 2)
{
remove(p[x]);
insert_l(p[y], x);
}
else if(op == 3)
{
for(int i = 0; i < idx; i++)
{
int t = l[i];
l[i] = r[i], r[i] = t;
}
}
else
{
for(int i = p[1], j = 1; j <= n; i = r[i], j++)
{
if(i == 1 || !i)
{
j--;
continue;
}
if(j < n) printf("%d ", e[i]);
else printf("%d\n", e[i]);
}
}
}
return 0;
}
G.涂色
题目链接:https://ac.nowcoder.com/acm/contest/8564/G
由于只能填0、1并且不能出现10,则可推出:按顺序填,一旦填了一个1,则后面只能是1。
所以,1可以出现的位置数量n(每个位置可出现,且各不相同同)+1(全0的情况)即为答案。
AC代码:
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
cout << n + 1 << endl;
return 0;
}
H.圆
题目链接:https://ac.nowcoder.com/acm/contest/8564/H
直接判断即可:
圆心的距离\(d = \sqrt{(x1 - x2)^2 + (y1 - y2)^2}\)
- 当\(d > r1 + r2\quad(d^2 > (r1 + r2)^2)\)或者\(d < |r1 - r2|\quad(d^2 < (r1 - r2)^2)\)时,无交点
- 否则有交点
AC代码:
#include <iostream>
using namespace std;
typedef long long LL;
int t;
LL x1, y1, r1, x2, y2, r2;
int main()
{
cin >> t;
while(t--)
{
cin >> x1 >> y1 >> r1 >> x2 >> y2 >> r2;
LL d = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
if(d > (r1 + r2) * (r1 + r2) || d < (r1 - r2) * (r1 - r2)) puts("NO");
else puts("YES");
}
return 0;
}
I.修改
题目链接:https://ac.nowcoder.com/acm/contest/8564/I
参考题解:牛客小白月赛29 - cumtljz - 博客园 (cnblogs.com)
要使数组所有数都变为0,则其差分数组都变为0。对于一个操作[l,r,w],可使差分数组第l位变为0,因此,如果所有位置都能与第n+1位联通,那么就存在一些操作让差分数组都变成0。那么如果将每个操作[l,r,w]视为一条边,求最小生成树即为最小解(注意:对原数组区间操作,影响的是差分数组的l和r+1位)。
AC代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 2e5+10, INF = 0x3f3f3f3f;
int n, m, l, r, w;
int p[N];
struct Edge
{
int l, r, w;
bool operator < (const Edge &W) const
{
return w < W.w;
}
}edges[N];
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
LL kruskal()
{
sort(edges, edges + m);
for(int i = 1; i <= n + 1; i++) p[i] = i;
LL res = 0, cnt = 0;
for(int i = 0; i < m; i++)
{
int a = edges[i].l, b = edges[i].r, w = edges[i].w;
a = find(a), b = find(b + 1);
if(a != b)
{
res += w;
cnt++;
p[a] = b;
}
}
if(cnt < n - 1) return INF;
else return res;
}
int main()
{
cin >> n >> m;
for(int i = 0; i < m; i++)
{
scanf("%d%d%d", &l, &r, &w);
edges[i] = {l, r, w};
}
LL t = kruskal();
if(t == INF) puts("-1");
else cout << t << endl;
return 0;
}
J.克隆
题目链接:https://ac.nowcoder.com/acm/contest/8564/J
参考题解:牛客小白月赛29 - cumtljz - 博客园 (cnblogs.com)
关于DFS序和欧拉序:dfs序和欧拉序 - Styx-ferryman - 博客园 (cnblogs.com)
AC代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5+10;
int n, m, k, ma;
int h[N], e[4 * N], ne[4 * N], idx;
int p[4 * N], cnt;
bool st[N];
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void dfs(int u)
{
st[u] = true;
p[cnt++] = u;
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(st[j]) continue;
dfs(j);
p[cnt++] = u;
}
}
int main()
{
cin >> n >> m >> k;
ma = (2 * n + k - 1) / k;
memset(h, -1, sizeof h);
for(int i = 0; i < m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
}
dfs(1);
puts("YES");
int num = cnt / ma, k = 0;
for(int i = 0; i < num; i++)
{
printf("%d", ma);
for(int j = 0; j < ma; j++)
printf(" %d", p[k++]);
putchar('\n');
}
if(cnt % ma)
{
printf("%d", cnt % ma);
for(; k < cnt; k++) printf(" %d", p[k]);
putchar('\n');
}
return 0;
}