C. Swiss Stage

分析:签到要注意BO1 和 BO3的小字。
cpp
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
#define ll long long
void solve(){
int x , y;
cin >> x >> y;
if(x == 0 && y ==1 )
cout << 4 << endl;
else if( x == 1 && y == 2){
cout << 4 << endl;
}else if(x == 0 && y == 0){
cout << 4 << endl;
}else if( x == 0 && y == 2){
cout << 6 << endl;
}else if( x == 1 && y == 0 ){
cout << 3 << endl;
}else if(x == 1 && y == 1){
cout << 3 << endl;
}else if(x == 2 && y == 0 ){
cout << 2 << endl;
}else if(x == 2 && y == 1){
cout << 2 << endl;
}else{
cout << 2 << endl;
}
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
solve();
return 0;
}
J. Graft and Transplant
分析:读题感觉和叶子节点有关,也就是非叶子节点的个数,非叶子节点的个数就等于操作的总次数,所以判断奇偶直接输出即可。
cpp
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define ll long long
void solve()
{
int n;
cin >> n;
vector<int> d(n + 1, 0);
for (int i = 0; i < n - 1; i++)
{
int u, v;
cin >> u >> v;
d[u]++;
d[v]++;
}
int cnt = 0;
for (int i = 1; i <= n; i++)
if (d[i] > 1)
cnt++;
if (n == 2)
{
cout << "Bob" << endl;
return;
}
if (cnt % 2 == 0)
cout << "Alice" << endl;
else
cout << "Bob" << endl;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
solve();
return 0;
}
E. Sheep Eat Wolves
分析:bfs求最短路并且要记录状态,不能贪心,因为没有办法模拟可能右边往左边运羊的情况。
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
// d[k][i][j] 表示船在 k岸(0为左岸, 1为右岸),左岸有 i只羊, j只狼时所需的最少趟数
int d[2][N][N];
// 定义一个别名ay,用于存储队列中的状态 {船的位置, 左岸羊数, 左岸狼数}
typedef array<int, 3> ay;
int main(){
// x: 羊总数, y: 狼总数, p: 船的最大载量, q: 安全阈值
int x, y, p, q;
cin >> x >> y >> p >> q;
// 初始化距离数组为无穷大 (0x3f3f3f3f 是一个常用的无穷大值)
memset(d, 0x3f, sizeof d);
// 设置初始状态:船在左岸(0),左岸有x只羊, y只狼,所需趟数为0
d[0][x][y] = 0;
// 创建BFS队列
queue<ay> Q;
// 将初始状态推入队列
Q.push({0, x, y});
// 开始广度优先搜索
while(Q.size()){
// 取出队首状态
auto [id, x0, y0] = Q.front();
Q.pop();
// 计算右岸的动物数量
// x1: 右岸羊数, y1: 右岸狼数
int x1 = x - x0, y1 = y - y0;
// 枚举本次过河船上搭载的动物组合
// i: 船上总动物数, j: 船上羊的数量
for (int i = 0; i <= p; i ++){ // i 代表船上总共的动物数量
// j 代表船上羊的数量, 那么狼的数量就是 i - j
// 还需要保证当前岸有足够的羊和狼可以上船
for (int j = 0; j <= i; j++){
// 船在左岸(id=0),要运走的羊(j)不能超过左岸的羊(x0)
// 船在右岸(id=1),要运走的羊(j)不能超过右岸的羊(x1)
bool enough_sheep = (id == 0 && j <= x0) || (id == 1 && j <= x1);
// 船在左岸(id=0),要运走的狼(i-j)不能超过左岸的狼(y0)
// 船在右岸(id=1),要运走的狼(i-j)不能超过右岸的狼(y1)
bool enough_wolves = (id == 0 && i - j <= y0) || (id == 1 && i - j <= y1);
if (!enough_sheep || !enough_wolves) continue;
// 计算移动后的新状态
// l_x0, l_y0: 移动后左岸的羊和狼的数量
int l_x0 = x0 + (id ? j : -j); // 如果船从右(1)来,左岸羊增加j;从左(0)走,减少j
int l_y0 = y0 + (id ? (i - j) : -(i-j)); // 如果船从右(1)来,左岸狼增加i-j;从左(0)走,减少i-j
// r_x1, r_y1: 移动后右岸的羊和狼的数量
int r_x1 = x - l_x0;
int r_y1 = y - l_y0;
// 检查移动后的状态是否安全
// 农民到了对岸,所以无人看管的是出发的那个岸
bool is_safe = true;
if (id == 0) { // 船从左岸出发,左岸无人看管
// 如果左岸有羊(l_x0 > 0),则必须满足狼数 <= 羊数 + q
if (l_x0 > 0 && l_y0 > l_x0 + q) is_safe = false;
} else { // 船从右岸出发,右岸无人看管
// 如果右岸有羊(r_x1 > 0),则必须满足狼数 <= 羊数 + q
if (r_x1 > 0 && r_y1 > r_x1 + q) is_safe = false;
}
// 如果状态安全,并且找到了更短的路径
if (is_safe && d[id ^ 1][l_x0][l_y0] > d[id][x0][y0] + 1)
{
// 更新最短趟数
d[id ^ 1][l_x0][l_y0] = d[id][x0][y0] + 1;
// 将新状态推入队列
Q.push({id ^ 1, l_x0, l_y0});
}
}
}
}
// 搜索结束后,寻找最终答案
int ans = 1e9; // 1e9 约等于无穷大
// 目标状态是:所有羊都在右岸(即左岸羊数为0),船在右岸(1)
// 此时左岸狼的数量可以是任意值,所以遍历所有可能
for (int i = 0; i <= y; i ++)
ans = min(ans, d[1][0][i]);
// 如果ans没有被更新过,说明无法达到目标状态
cout << (ans >= 1e9 ? -1 : ans) << endl;
return 0;
}
K. Maximum Rating
分析:权值线段树算是比较板的题目了。
核心就是知道正数的个数一定是可以的,这是一种情况,然后我们想知道有负数可以抵消多少次正数,最容易抵消的一次增加的可能就是 |负数和| > 最小的整数 , 其次就是 |负数和| > 最小的前两个整数 ... 其次类推 , 能抵消几次结果就是 1 + 抵消的次数。 因此我们可以直接知道数组的和是sum,然后利用权值线段树可以快速求出数组中最少需要多少个数的和>=sum,算出来的结果是c (这个c一定是某几个最大的正数的和, 剩下的整数就是会被抵消掉的正数和,|负数和| >= 没有选的正数), 正数的个数 - c + 1就是可以被抵消的正数和,也就是最后的结果。
cpp
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define ll long long
const ll N = 200010, INF = 1e9;
struct SegTree
{
ll l, r, cnt, sum;
} tr[N << 5];
ll tot;
void update(ll &u, ll l, ll r, ll d, ll cnt) // d:需要加入的数 cnt:需要加入的数的个数
{
if (!u) // 动态开点
u = ++tot;
tr[u].cnt += cnt; // 类似于pushup的作用
tr[u].sum += d * cnt;
if (l == r)
return;
ll mid = l + r >> 1;
if (d > mid)
{
update(tr[u].r, mid + 1, r, d, cnt);
}
else
{
update(tr[u].l, l, mid, d, cnt);
}
}
ll query(ll u, ll l, ll r, ll sum)
{ // 求和为 sum 需要的最少的数的数量
if (l == r)
{
return (sum <= 0 ? 0 : (sum + l - 1) / l);
}
ll mid = l + r >> 1;
if (tr[tr[u].r].sum >= sum) // 因为右边存的是比较大的数,如果右边的数的和>sum,则直接在右边找
return query(tr[u].r, mid + 1, r, sum);
else // 否则先把右边比较大的数拿走,再去左边找剩下的需要的sum
return query(tr[u].l, l, mid, sum - tr[tr[u].r].sum) + tr[tr[u].r].cnt;
}
void solve()
{
ll n, q;
cin >> n >> q;
ll rt = 0, cnt = 0, sum = 0;
vector<ll> a(n + 1);
for (ll i = 1; i <= n; i++)
{
cin >> a[i];
if (a[i] > 0)
update(rt, 1, INF, a[i], 1), cnt++;
sum += a[i];
}
while (q--)
{
ll x, v;
cin >> x >> v;
if (a[x] > 0)
update(rt, 1, INF, a[x], -1), cnt--;
if (v > 0)
update(rt, 1, INF, v, 1), cnt++;
sum += v - a[x];
a[x] = v;
cout << cnt - query(1, 1, INF, sum) + 1 << endl;
// |负数和| >= 没有选的正数 (query贪心选小的正数)
// cnt - query(1, 1, INF, sum) 求出的就是负数的和可以抵消的最多的正数和
// + 1 是加上了抵消了 0 个正数的情况
}
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
solve();
return 0;
}