2022-04-13每日刷题打卡
代码源——每日一题
弗拉德和糖果 II - 题目 - Daimayuan Online Judge
不久前,弗拉德过生日,他收到了一包糖果。有 n 种糖果,第 i 种糖果有 i 个(1≤i≤n)。
弗拉德决定每次只吃一个糖果。为了从吃东西中获得最大的乐趣,弗拉德不想连续吃两个相同类型的糖果。
帮助他弄清楚他是否可以在不连续吃两个相同的糖果的情况下吃掉所有的糖果。
简而言之,给定 n 个正整数 ai,aiai 表示有 ai 个 i,找到是否存在一种序列,使得所有的数都用上,且不存在 i 连续的情况
输入格式:
第一行,包含一个整数 n。 第二行,包含 n 个正整数。
输出格式:
输出一行,如果存在,输出YES
,否则输出NO
样例输入
2
1 1
样例输出
YES
说明
只有两种情况:
1 2
2 1
无论先吃哪种糖果,都能吃完且不连续吃相同类型的糖果
数据限制
对于 100% 的数据,保证 1≤n≤5000000,1≤ai≤2^30
这是一道思维题,我们只用看最大值与其他值的和的关系就行:
当最大值大于其它值的和+1时,哪怕我们吃一口其它的糖,再吃一口最大数量的糖,最后也会剩下最大值的种类的糖,这样就必然要连着吃两个一样,所以要输出NO。
当最大值等于其它值的和+1时,我们只要吃一口最大数量的糖,再吃一口其它数量的糖即可。这样就能正好吃完。输出YES。
当最大值小于其它值的和+1时,我们可以先把其他数量的糖果内部消化到总和和最大值数量相等,这样就是第二种情况了。输出YES。
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int MOD = 1e9 + 7, N = 1e6 + 10;
inline int read() {
int x = 0; char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x;
}
int main()
{
int n;
ll sum = 0, mx = 0,num;
n = read();
for (int i = 0; i < n; i++)
{
num = read();
if (mx < num)
{
sum += mx;
mx = num;
}
else sum += num;
}
if ((n == 1 && mx == 1) || mx <= sum + 1)
{
puts("YES");
}
else
{
puts("NO");
}
return 0;
}
CodeForces——线段树专题
D - Segment Tree, part 1 - Codeforces
In this task, you need to add to the segment tree the operation of finding for the given xx and ll the minimum index j such that j≥l and a[j]≥x.
Input
The first line contains two integers n and mm (1≤n,m≤1000001≤n,m≤100000), the size of the array and the number of operations. The next line contains n numbers ai, the initial state of the array (0≤ai≤10^9). The following lines contain the description of the operations. The description of each operation is as follows:
- 1 i v: change the item with index i to vv (0≤i<n, 0≤v≤10^9).
- 2 x l: find the minimum index j such that j≥l and a[j]≥x (0≤x≤10^9, 0≤l<n). If there is no such element, print −1. Indices start from 0.
Output
For each operation of the second type, print the answer for the query.
Example
input
5 7
1 3 2 4 3
2 3 0
2 3 2
1 2 5
2 4 1
2 5 4
1 3 7
2 6 1
output
1
3
2
-1
3
这题意思是说,给你一个数组,有两种操作:
第一种:把下标为i的元素改为y。
第二种:找到第一个大于等于下标x,且元素大于等于y的元素。如果没有,输出-1
构造一个线段树,每个节点f[k]的意思是:当前区间内的最大值。这样我们就可以根据左右子节点的情况来判断我们要去左边还是右边了。当左节点大于y时,我们去左边找,反之去右边找,如果没有大于等于y的元素,就返回-1。
但是!这样是有问题的,就算左边的节点大于y,但是有可能这个大于y的节点的下标是小于x的,这样显然不合规矩。此时你说,那我能不能同时记录再记录一下这个最大值的下标,这样也会有问题,比如2 4 3 5 1这个数组,我们要找下标大于等于1,元素大于等于2的元素,此时左边的最大值是4,下标是2,你一看,“哦这样不行我去右边找吧”,然后你找到了第三个元素,但答案是第一个元素。
这问题的解决方法就是我们应该造一个特殊的线段树,能够回溯的线段树,这样我们被“骗”去左边后还能再有次机会去右边找。具体该怎么做?
我们可以每次准备一个变量res接受结果,如果左边的指小于目标值,自然就不用去左边找了,但如果左边值大于目标值,我们就要去左边找,然后用res接收返回的结果,当我们找到叶节点后,发现这个节点不满足条件,我们被“骗”了,就返回一个-1回去。当res接收到-1时,我们就知道被骗了,再去右边找。如果没被骗,就返回下标给res。
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#define endl '\n';
typedef long long ll;
typedef pair<int, int> PII;
const int N = 100050;
int f[4 * N], a[N];
void buildtree(int k,int l,int r)
{
if (l == r)
{
f[k] = a[l];
return;
}
int m = (l + r) / 2;
buildtree(k + k, l, m);
buildtree(k + k + 1, m+1, r);
f[k] = max(f[k + k], f[k + k + 1]);
}
void revise(int k, int l, int r, int x, int y)
{
if (l == r)
{
f[k] = y;
return;
}
int m = (l + r) / 2;
if (x <= m)revise(k + k, l, m, x, y);
else revise(k + k + 1, m + 1, r, x, y);
f[k] = max(f[k + k] , f[k + k + 1]);
}
int quire(int k, int l, int r, int x, int y)
{
if (l == r)
{
if (f[k] >= y && l >= x)return l;
else return -1;
}
int m = (l + r) / 2;
int res = -1;
if (f[k + k] >= y && m >= x) res = quire(k + k, l, m, x, y);
if (res == -1) res = quire(k + k + 1, m + 1, r, x, y);
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)cin >> a[i];
buildtree(1, 1, n);
while (m--)
{
int st, x ,y;
cin >> st;
if (st == 1)
{
cin >> x >> y;
revise(1, 1, n, x + 1, y);
}
else
{
cin >> y >> x;
int res = quire(1, 1, n, x+1,y);
if (res != -1)res--;
cout << res << endl;
}
}
return 0;
}