专栏文章
题解:P8539 「Wdoi-2」来自地上的支援
P8539题解参与者 6已保存评论 5
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 5 条
- 当前快照
- 1 份
- 快照标识符
- @mio4yoa1
- 此快照首次捕获于
- 2025/12/02 13:26 3 个月前
- 此快照最后确认于
- 2025/12/02 13:26 3 个月前
P8539 「Wdoi-2」来自地上的支援题解
省流:线段树好题(好像可以做,但蒟蒻太菜啦)
题目大意
有 个核反应机组,第 个机组的初始活动强度为 。依次进行 次操作,第 次操作会在前 个机组中选择当前活动度最高的机组(规则详见题目),并将其活动度增加 。
现在有 次询问,每次询问给出 ,要求找到最小的非负整数 ,使得将第 个机组的初始活动度改为 后,该机组至少被选中 次。如果不存在这样的 ,输出 。
算法思路
观察
因为第 个数必须被选中 次,根据题意,不难发现这 次选中一定是从第 次操作连续 个被选中,所以如果一个数在前面没选中,后面就更不可能了。
数学推导
对于位置 要至少被选中 次,需要满足:
- 对于前面的要求: 必须 ≥ 前 次操作后的最大值
- 在区间里的要求:对于 ,需要满足:
线段树优化
使用线段树维护 的最大值,可以快速计算:
对于所有 ,需要:
代码
CPP#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2000010;
int n, m, v;
int a[N];
ll pre[N], ans1, ans2;
ll tr[4 * N];//线段树
void build(int u, int l, int r)
{
if (l == r)
{
tr[u] = a[l] - (ll)l * v;
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
tr[u] = max(tr[u << 1], tr[u << 1 | 1]);
}
ll query(int u, int l, int r, int L, int R)
{
if (L > R)
return -1e18;
if (L <= l && r <= R)
return tr[u];
int mid = (l + r) >> 1;
ll res = -1e18;
if (L <= mid)
res = max(res, query(u << 1, l, mid, L, R));
if (R > mid)
res = max(res, query(u << 1 | 1, mid + 1, r, L, R));
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m >> v;
for (int i = 1; i <= n; i++)
cin >> a[i];
build(1, 1, n);//构建线段树
ll cur = 0;//预处理最大值
for (int i = 1; i <= n; i++)
{
pre[i] = cur;
if (a[i] > cur)
cur = a[i];
cur += v;
}
while (m--)
{
int x, k;
cin >> x >> k;
if (x + k - 1 > n)//无法选择k次
{
continue;
}
ll s = pre[x];
if (k > 1)
{
// 查询区间 [x+1, x+k-1] 的最大值
ll qval = query(1, 1, n, x + 1, x + k - 1);
s = max(s, qval + (ll)x * v + 1);
}
ans1 ^= s;
ans2 += s;//统计答案
}
cout << ans1 << " " << ans2;
return 0;
}
完结撒花~~
相关推荐
评论
共 5 条评论,欢迎与作者交流。
正在加载评论...