Educational Codeforces Round 143 (Rated for Div. 2)

Problem - B Ideal Point

给定n个线段区间\([l,r]\),我们定义\(f(x)\)为覆盖点\(x\)的线段数,我们每次操作可以删除任意一条线段,并且操作数不限,给出q次询问,每次询问点x能否通过操作使得\(f(x)\)严格最大

题解:贪心

我们通过模拟发现只要把不包含x的线段全部删除,那么仍然存在的线段都会限制\(f(x)\),然后如果存在其他点y使得\(f(y)>=f(x)\)或者存在,那么说明我们无论如何都不能使得\(f(x)\)最大,反之一定可以,由于数据比较小,直接通过计数数组\(cnt\)判断即可

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10; int n, k;
pii a[55]; void solve()
{
cin >> n >> k;
for (int i = 1; i <= n; ++i)
cin >> a[i].first >> a[i].second;
vector<int> cnt(55, 0);
int ok = 0;
for (int i = 1; i <= n; ++i)
{
if (a[i].first <= k && k <= a[i].second)
{
for (int j = a[i].first; j <= a[i].second; ++j)
cnt[j]++;
}
}
int maxx = -INF, num = 0;
for (int i = 1; i <= 50; ++i)
maxx = max(cnt[i], maxx);
for (int i = 1; i <= 50; ++i)
if (cnt[i] == maxx)
num++;
if (cnt[k] == maxx && num == 1)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}

Problem - C Tea Tasting

给定n杯茶,每杯茶有\(a_i\)毫升,每杯茶面前都有一个品茶人,每次能够喝\(b_i\)毫升的茶,每一轮品茶结束后所有人都会往左移动,也就是说第一回合结束后第一个人不会再喝茶,第二回合结束后第二个人不会再喝茶,那么每个人在每回合能够喝到茶的数量为\(min(a_i,b_i)\),请你求出所有人分别能够喝到的茶的数量

题解:二分+前缀和+差分

首先我们可以知道对于第\(i\)杯茶来说,只有\([i+1,n]\)这些人能够喝到,所以我们可以先求出每个人喝茶\(b_i\)的前缀和\(pre\),对于每杯茶我们只需要二分前缀和找到后面第一个喝不满\(b_j\)这个茶的人\(j\),那么我们只要让\(j\)喝完剩下所有茶,然后利用差分数组记录中间都能喝满自己能喝的茶的人即可;

注意:这边在二分前缀和的时候有个技巧,我们对于每个\(a_i\)都要去往\(i\)的后面找,也就是说所有\(i\)后面的前缀和都要减去\(pre[i-1]\),比较麻烦,那么我们这边大佬有个技巧就是我们直接\(a_i+pre[i-1]\),这样效果也是一样,我只能说太秀

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10; int n;
int pre[N]; void solve()
{
cin >> n;
vector<int> ans(n + 10);
vector<int> a(n + 10), b(n + 10);
vector<int> dif(n + 10);
for (int i = 1; i <= n; ++i)
cin >> a[i];
for (int i = 1; i <= n; ++i)
cin >> b[i];
for (int i = 1; i <= n; ++i)
pre[i] = pre[i - 1] + b[i];
for (int i = 1; i <= n; ++i)
{
int pos = upper_bound(pre + 1, pre + n + 1, a[i] + pre[i - 1]) - pre;
ans[pos] += a[i] - (pre[pos - 1] - pre[i - 1]);
dif[i]++;
dif[pos]--;
}
for (int i = 1; i <= n; ++i)
dif[i] = dif[i - 1] + dif[i];
for (int i = 1; i <= n; ++i)
{
ans[i] += b[i] * dif[i];
cout << ans[i] << " ";
}
cout << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}

Problem - D Triangle Coloring

给定n个节点和n条边,每条边存在权值,并且\(6|n\),将每三个节点变成一个三元组,并用三条边连接,现在需要将n个点中一半的点涂成红色,一半的点涂成蓝色,如果两个点颜色不一样,那么连接这两个点的边的权值会被记录在sum中,最后你需要使得sum最大,并且求出使得sum最大的方案数

题解:组合计数+思维

首先n个点中一半为红色,一半为蓝色,很明显在一个三元组内,我们只有两种涂色方式:

  1. 全为红色或蓝色;
  2. 一红二蓝或者一蓝二红;

显然后者对于sum的贡献更大,我们取后者

假设一个三元组需要涂一红二蓝,也就是说我们在每个三元组中会选择两条边,那么存在以下几种情况:

  1. 每条边的权值都不一样,我们肯定会选择最大的两条,那么只有一种选择
  2. 最小两条边的权值相同,那么我们肯定会选择最大的以及任意一条最小边,有两种选择;
  3. 如果三条边的权值都相同,我们任意选择两条边即可,有3种选择;

那儿我们还需要知道哪些三元组涂了一红二蓝,哪些涂了一蓝二红,答案乘上\(C_{n/3}^{n/6}\)即可

注意:因为取模运算没有除法,所以我们需要利用快速幂求出乘法逆元,\(C_{n/3}^{n/6} = \frac{A_{n/3}^{n/6}}{A_{n/6}^{n/6}}\)

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-9;
const int N = 3e5 + 10, M = 4e5 + 10; int qpow(int x, int y)
{
int res = 1;
while (y)
{
if (y & 1)
res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
} int A(int n, int m)
{
int res = 1;
for (int i = m; i > m - n; --i)
res = res * i % mod;
return res;
} int n;
int a[4]; void solve()
{
cin >> n;
int ans = 1;
for (int i = 1; i <= n; i += 3)
{
cin >> a[1] >> a[2] >> a[3];
sort(a + 1, a + 4);
if (a[1] == a[2] && a[2] == a[3])
ans = ans * 3 % mod;
else if (a[1] == a[2])
ans = ans * 2 % mod;
}
ans = ans * A(n / 6, n / 3) % mod;
ans = ans * qpow(A(n / 6, n / 6), mod - 2) % mod;
cout << ans << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}

Problem - E Explosions?

给定n个怪兽,每个怪兽血量为\(h_i\),你现在有两种魔法:

  1. 消耗1MP,对一个怪兽的血量减去1,可以释放无数次
  2. 消耗xMP,对于一个怪兽的血量减去x,如果该怪兽死亡,会引起连锁反应,他会对它相邻两边的怪兽产生爆炸伤害\(h_i-1\),如果该伤害继续造成死亡,会继续往旁边造成爆炸伤害,直到没有造成死亡为止,只能在最后一次释放

注意:怪物的位置不能移动,即使怪物死了,也不会消失

请你求出最少需要多少MP才能消灭所有怪物

题解:维护二元单调栈+贪心 :好题目

根据贪心思想,我们最好在最后一步爆炸解决掉所有的怪兽,所以我们可以枚举在每一个怪兽位置爆炸需要的MP,最后取min即可

那么为了实现最后的爆炸能够清除所有的怪兽,我们需要保证数组h要形成一个单峰,引爆位置左边严格递增,引爆位置右边严格递减,那么假设使得左边严格递增的花费为\(L[i]\),右边严格递减的花费为\(R[i]\),那么选择i位置作为引爆点的全部花费为\(L[i]+R[i]+h[i]\)

现在的关键就是如何求出L[i]和R[i],我们先对于L[i]进行讨论,R[i]可以同理得出:

我们可以维护一个单调递增栈,栈中元素是个二元组\((val,cnt)\)表示一个集合:val代表集合中最大值,cnt代表集合中有cnt个数,并且该集合是一个严格递增且差值为1的集合,那么很明显我们可以知道集合中的最小值为\(val-cnt+1\)

那么我们遍历数组\(h\)维护单调递增栈,我们讨论以下情况对单调栈内的集合进行合并和插入:

  1. 如果当前集合的最小值\(val-cnt+1>\)栈顶集合的最大值,直接将插入集合\((h[i],1)\)

  2. 如果当前集合的最小值\(val-cnt+1<=\)栈顶集合的最大值,那么我们需要将集合进行合并,合并的过程需要代价,并将栈顶不断出栈,直到不能合并为止,比如栈顶为(3,1),待插入的集合为(2,1),那么合并后的集合应该为(2,2),所以花费应该为3-2=1,因为我们需要的是一个单调递增且差值为1的集合

再举个例子,(3,3)和(3,2)合并,也就是(1,2,3)和(2,3)合并,那么我们需要的代价就是为了方便先把(1,2,3)变为(0,0,0)然后再变成(0,0,1),所以需要的代价为(1+2+3-1),那么这个代价我们可以利用等差数列求和快速实现

根据这样的操作我们就能够计算出L[i],同理我们从后往前遍历维护单调递增栈,即可求出\(R[i]\)

下面给出一个样例,并附上图示,便于理解集合的合并和为什么这样能够得出L[i]和R[i]:

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 3e5 + 10, M = 4e5 + 10; int n;
pii stk[N];
int h[N], L[N], R[N];
int tt; int cal(int x, int n)
{
int p = max(0ll, x - n + 1);
return n * (p + x) / 2;
} void solve()
{
cin >> n;
tt = 0;
for (int i = 1; i <= n; ++i)
cin >> h[i];
int sum = 0;
for (int i = 1; i <= n; ++i)
{
int cnt = 1;
while (tt && h[i] - cnt + 1 <= stk[tt].first)
{
int minn = max(0ll, h[i] - cnt + 1); //防止最小值为负数
sum += cal(stk[tt].first, min(stk[tt].first, stk[tt].second));
sum -= cal(max(0ll, minn - 1), min(max(0ll, minn - 1), stk[tt].second));
cnt += stk[tt].second;
tt--;
}
L[i] = sum;
stk[++tt] = mpk(h[i], cnt);
}
tt = 0;
sum = 0;
for (int i = n; i >= 1; --i)
{
int cnt = 1;
while (tt && h[i] - cnt + 1 <= stk[tt].first)
{
int minn = max(0ll, h[i] - cnt + 1);
sum += cal(stk[tt].first, min(stk[tt].first, stk[tt].second));
sum -= cal(max(0ll, minn - 1), min(max(0ll, minn - 1), stk[tt].second));
cnt += stk[tt].second;
tt--;
}
R[i] = sum;
stk[++tt] = mpk(h[i], cnt);
}
int ans = INF;
for (int i = 1; i <= n; ++i)
ans = min(ans, h[i] + L[i] + R[i]);
cout << ans << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}

Educational Codeforces Round 143 (Rated for Div的更多相关文章

  1. Educational Codeforces Round 60 (Rated for Div. 2) - C. Magic Ship

    Problem   Educational Codeforces Round 60 (Rated for Div. 2) - C. Magic Ship Time Limit: 2000 mSec P ...

  2. Educational Codeforces Round 60 (Rated for Div. 2) - D. Magic Gems(动态规划+矩阵快速幂)

    Problem   Educational Codeforces Round 60 (Rated for Div. 2) - D. Magic Gems Time Limit: 3000 mSec P ...

  3. Educational Codeforces Round 43 (Rated for Div. 2)

    Educational Codeforces Round 43 (Rated for Div. 2) https://codeforces.com/contest/976 A #include< ...

  4. Educational Codeforces Round 35 (Rated for Div. 2)

    Educational Codeforces Round 35 (Rated for Div. 2) https://codeforces.com/contest/911 A 模拟 #include& ...

  5. Codeforces Educational Codeforces Round 44 (Rated for Div. 2) F. Isomorphic Strings

    Codeforces Educational Codeforces Round 44 (Rated for Div. 2) F. Isomorphic Strings 题目连接: http://cod ...

  6. Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes

    Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes 题目连接: http://code ...

  7. Educational Codeforces Round 63 (Rated for Div. 2) 题解

    Educational Codeforces Round 63 (Rated for Div. 2)题解 题目链接 A. Reverse a Substring 给出一个字符串,现在可以对这个字符串进 ...

  8. Educational Codeforces Round 39 (Rated for Div. 2) G

    Educational Codeforces Round 39 (Rated for Div. 2) G 题意: 给一个序列\(a_i(1 <= a_i <= 10^{9}),2 < ...

  9. Educational Codeforces Round 48 (Rated for Div. 2) CD题解

    Educational Codeforces Round 48 (Rated for Div. 2) C. Vasya And The Mushrooms 题目链接:https://codeforce ...

  10. Educational Codeforces Round 60 (Rated for Div. 2) 题解

    Educational Codeforces Round 60 (Rated for Div. 2) 题目链接:https://codeforces.com/contest/1117 A. Best ...

随机推荐

  1. 【USACO 2021 February Contest, Platinum】Problem 1 No Time to Dry

    \(\text{Solution}\) 一个点可与另一个颜色相同点同时涂色当且仅当两点间颜色都大于等于这两点 那么我们可以预处理一个点向左向右最远能到的位置,记为 \(l_i,r_i)\) 当 \(l ...

  2. Android实现仿微信实时语音对讲功能|与女友游戏开黑

    与亲朋好友一起玩在线游戏,如果游戏中有实时语音对讲能力就可以拉进玩家之间的距离,添加更多乐趣.我们以经典的中国象棋为例,开发在线语音对讲象棋.本文主要涉及如下几个点: 在线游戏的规则,本文以中国象棋为 ...

  3. Quill编辑器实现原理初探

    简介 从事前端开发的同学,对富文本编辑器都不是很陌生.但是大多数富文本编辑器都是开箱即用,很少会对其实现原理进行深入的探讨.假如静下心去细细品味,会发现想要做好一款富文本编辑器,需要对整个前端生态有较 ...

  4. Shell命令-基础

    Shell命令 1 变量 定义变量时,变量名不加美元符号$,注意,变量名和等号之间不能有空格 a="myname" 用语句给变量赋值时, for file in `ls /etc` ...

  5. 基于Docker使用CTB生成地形切片并加载

    1. 引言 CTB(Cesium Terrain Builder)是一个用于地形切片的C++编写的命令行工具 GitHub地址为:GitHub - geo-data/cesium-terrain-bu ...

  6. C#的闭包捕获变量与英语中Nice to meet you的联系

    看标题有种"意大利面与42号混凝土"放在一起说的感觉,实际上,就是. 闭包捕获变量 我们都知道在C#里,闭包捕获的是变量,而不是变量值本身 每个Task在运行的时候,发现i的值是3 ...

  7. 【C++复习】运算符重载中的特殊运算符

    无法被重载 类属关系运算符 . 成员指针运算符 .* 作用域分辨符 :: 三目运算符 ?: 只能通过成员函数重载 赋值运算符= 方括号[] 圆括号() 指向结构体成员运算符->

  8. windows mongo 开启副本集 6.x版本 mongo : 无法将“mongo”项识别为 cmdlet、函数、脚

    mongo报错 当前使用版本6.0.3,bin目录下并没有mongo.exe,所以没有mongo命令, 需要下载 https://www.mongodb.com/try/download/shell  ...

  9. 查看git的用户名和密码

    转载自:https://www.cnblogs.com/xihailong/p/13354628.html 一.查看查看用户名 :git config user.name查看密码: git confi ...

  10. MapReduce原理——Shuffle机制

    在Map方法之后,Reduce方法之前的数据处理过程称之为Shuffle. Map方法输出的数据会获得对应的分区,进入环形缓冲区(缓冲区一半写索引,另一半写数据).数据达到缓冲区的80%会发生溢写.在 ...