LG 题解 P4041 [AHOI2014/JSOI2014]奇怪的计算器
目录
- 前置芝士
- Description
- Solution
- Code
前置芝士
- 线段树
Description
简述题意:
给你一段序列 \(a\),要求支持下面 \(4\) 种操作,设操作完后的序列为 \(c\),输出操作完后的序列 \(c\)。
1、全局加 \(x\);
2、全局减 \(x\);
3、全局乘 \(x\);
4、全局加上该位置的起始值乘 \(x\);
还有一个限制值域 \([L,R]\),如果某个元素操作完后值 \(>R\),将该元素值变为 \(R\),如果 \(,变为 \(L\)。
Solution
四个操作都挺好说,但加上值域限制后就……
发现对原序列排序不会造成影响。
我们将其从小到大排序。
并且我们发现,不论执行那个操作,排序后序列每个元素之间的大小关系仍然是不变的。
所以每次操作后,大于 \(R\) 或者小于 \(L\) 的元素只会聚集在左右端点,对他们进行区间覆盖操作即可。
这里提供一个操作的小 trick,我们设一个更新函数 \(f(p, k_1, k_2, k_3)\) 表示 \(c_i = c_i * k_1 + a_i * k_2 + k_3\)。
更新的时候可以方便的对应五个操作(包含区间覆盖)
- 全局加减,\(f(p,1,0,x)\);
- 全局乘,\(f(p,x,0,0)\);
- 全局加起始值的 \(x\) 倍,\(f(p,1,x,0)\);
- 区间覆盖,\(f(p,0,0,L/R)\)。
时间复杂度 \(O(n \log n)\)。
Code
/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include
#include
#include
#include
#include
#include
#define int long long
#define orz cout<<"lkp AK IOI!"<> 1;
Build(lson, l, mid), Build(rson, mid + 1, r);
Push_up(i);
}
void Modify1(int i, int l, int r) {
if(l == r) { Push_now(i, 0, 0, limL); return ; }
Push_down(i);
int mid = (l + r) >> 1;
if(tree[rson].min < limL) Push_now(lson, 0, 0, limL), Modify1(rson, mid + 1, r);
else Modify1(lson, l, mid);
Push_up(i);
}
void Modify2(int i, int l, int r) {
if(l == r) { Push_now(i, 0, 0, limR); return ; }
Push_down(i);
int mid = (l + r) >> 1;
if(tree[lson].max > limR) Push_now(rson, 0, 0, limR), Modify2(lson, l, mid);
else Modify2(rson, mid + 1, r);
Push_up(i);
}
void Query(int i, int l, int r) {
if(l == r) { ans[a[l].bh] = tree[i].max; return ; }
Push_down(i);
int mid = (l + r) >> 1;
Query(lson, l, mid), Query(rson, mid + 1, r);
}
}
signed main()
{
n = read(), limL = read(), limR = read();
for(int i = 1; i <= n; ++i) {
char opt; cin >> opt;
if(opt == '+') q[i].opt = 1;
else if(opt == '-') q[i].opt = 2;
else if(opt == '*') q[i].opt = 3;
else q[i].opt = 4;
q[i].val = read();
}
m = read();
for(int i = 1; i <= m; ++i) a[i].val = read(), a[i].bh = i;
sort(a + 1, a + m + 1);
Seg::Build(1, 1, m);
for(int i = 1; i <= n; ++i) {
// cout< limR) Seg::Modify2(1, 1, m);
}
Seg::Query(1, 1, m);
for(int i = 1; i <= m; ++i) printf("%lld\n", ans[i]);
return 0;
}