本文已参与[新人创作礼]活动,一起开启掘金创作之路
题目描述
这是4月14日代码源div2的每日一题。
上帝的集合 - 题目 - Daimayuan Online Judge
题目描述
现在上帝有一个空集合,现在他命令你为他执行下列三种操作 n 次,他每次会给你一个操作类型 op。
操作1:向集合中插入一个整数 x;
操作2:将集合中所有的数加上 x;
操作3:输出集合中最小的数,并从集合中将他删除,如果存在多个最小的整数,任意选择一个即可;
输入描述
第一行输入一个整数 n;
接下来的 n 行,每行的输入如下所示。第一个数代表 op,如果 op=1 或 op=2,第二个数代表 xi:
1 xi
2 xi
3
输出描述
如果 op=3,请输出集合中的最小值。
样例输入
7
1 2
1 1
3
1 3
2 5
3
3
样例输出
1
7
8
数据范围
2≤n≤10^6, 1≤xi≤10^12
问题解析
第一个操作:向集合中插入值。这个操作并不难,有非常多的方法都能做到。
第三个操作:输出最小值并删除。我们利用优先队列或mulset等容器都可以很方便的做到。
问题就是第二个操作,怎么能在短时间内把所有的值都加上1?说到区间修改有的人可能会想到线段树,确实,线段树是个可行的方法,加上lz标记后,我们可以在logn的时间内做到区间修改,并且只要每个点存储的都是区间内的最小值,我们就可以在logn的时间内完成区间修改,logn的时间内找到最小值并将其删除,logn的时间内插入一个元素。
但是lz标记这种东西写起来我觉得太麻烦了!我们有没有办法能做到更简便的完成操作二?
当然是有的,我才不想跑去写lz标记。我们可以假装把所有的元素都加上x,我们用一个变量add存下所有加在全体的x,只要第三次操作输出值的时候把add加上那个值即可。那么有个问题,比如我前面先插入几个元素后全体加10,然后插入了一个很小的元素,这样操作三会输出这个很小的元素,输出的时候会加上add,但是这个元素在逻辑上并没有加上10,怎么办?好解决,我们只要插入的时候,把元素预先减去add再插入即可,比如我这里插入的11,但实际插入的是1。这样在再输出的也是11。至于操作一和三,我们只要找一个能很快找到最小值的容器就行。
AC代码
1:优先队列(小根堆)
#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 N = 1e6 + 10;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll t,add=0;
priority_queue<ll,vector<ll>,greater<ll>>que;
cin >> t;
while (t--)
{
int st;
cin >> st;
if (st == 1)
{
ll x;
cin >> x;
que.push(x-add);
}
else if (st == 2)
{
ll x;
cin >> x;
add += x;
}
else
{
cout << que.top()+add << endl;
que.pop();
}
}
return 0;
}
2.线段树
这个有点特殊,因为线段树是很难直接加入一个新的点的,但我们可以提前开好空间,这里一共操作数是10^6,假设这全是插入操作,加上保险起见,我们最多开他一个4* 1e6+40的线段树,一共有1e6+10个叶子,为了不影响结果,我们把所有的叶子都整成很大的值。然后照着这些位置一个个插入值即可,至于删除操作,我们直接把那个叶子的值改成很多的值即可。
#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 N = 1e6 + 10;
ll f[4 * N],len=0;
void inserttree(ll k, ll l, ll r, ll x,ll y)
{
if (l == r)
{
f[k] = y;
return;
}
ll m = (l + r) / 2;
if (x <= m)inserttree(k + k, l, m, x, y);
else inserttree(k + k + 1, m + 1, r, x, y);
f[k] = min(f[k + k], f[k + k + 1]);
}
ll calc(ll k, ll l, ll r)
{
if (l == r)
{
ll ans = f[k];
f[k] = 1e15;
return ans;
}
ll m = (l + r) / 2,res;
if (f[k + k]<= f[k + k + 1])res = calc(k + k, l, m);
else res = calc(k + k + 1, m + 1, r);
f[k] = min(f[k + k], f[k + k + 1]);
return res;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll t,add=0;
cin >> t;
memset(f, 0x3f3f, sizeof f);
int a = 0;
while (t--)
{
ll st, x;
cin >> st;
if (st == 1)
{
cin >> x;
len++;
inserttree(1, 1, 1000000, len, x - add);
}
else if (st == 2)
{
cin >> x;
add += x;
}
else
{
cout << 1LL*(calc(1, 1, 1000000)+add) << endl;
}
}
return 0;
}