XJYUOJ #1053 nocriz与队列计算机
题目描述
nocriz是一位来到了西安的同学。
他在参观兵马俑时,了解到在遥远的秦朝,为了进行复杂的计算,秦始皇发明了队列计算机。兵马俑就是秦始皇为了在地下进一步发展科技而制作的队列计算机模型。
在此题中,队列计算机被简化成长度为\(n\)的一个士兵的队列,其中计算元件(士兵)有两种,其中\(a_i\)为这个士兵的特征值。
- 加法士兵负责从左边读入一个数字,并将\((x+ a_i)\) \(mod\) \(998244353\) 告诉下一个士兵
- 乘法士兵负责从左边读入一个数字,并将\((x\ast a_i)\) \(mod\) \(998244353\) 告诉下一个士兵
每一次,秦始皇有两个操作:
- 更换一个士兵(位置为\(p\)),给出新士兵的类型和特征值
- 告诉第\(l\)个士兵一个数\(x\),求第\(r\)个士兵说出的数字
秦始皇了解到nocriz学习了数据结构,要考考nocriz同学,但是他把锅丢给了你。
输入格式
一行两个整数\(n,q\); 接下来\(n\)行,每行两个整数\(typ_i,a_i\)。
- \(typ_i=0\) 士兵为乘法士兵。
- \(typ_i=1\) 士兵为加法士兵。
接下来\(q\)行,每行开始一个整数\(op\)
- \(op=0\)接下来输入\(p,typ_p,a_p\),代表修改操作
- \(op=1\)接下来输入\(l,r,x\),代表询问操作
输出格式
对于每次询问操作,输出一行一个数代表答案。
数据范围与提示
\(n,q\leq 5*10^5\)
所有出现过的数字在\([1,998244352]\)区间中
思路
乍一看问题好像很复杂,又是加又是乘,每次输入的数还不一样。
为了方便起见,我们规定一个士兵有两个操作,先乘上一个数,再加上一个数。大家可能会说这不是更复杂了嘛,但是,这对我们之后的模式化处理是有帮助的。
所以,对于乘法士兵,他的乘法值是\(a_i\),加法值是\(0\);对于加法士兵,他的乘法值是\(1\),加法值是\(a_i\)。
我们先来考虑只有两个士兵的情况,我们设第一个士兵乘法标记为\(a_1\),加法标记为\(b_1\),第二个士兵乘法标记为\(a_2\),加法标记为\(b_2\)。
如果我们输入\(x\),那么输出为\((a_1*x+b_1)*a_2+b_2=a_1*a_2*x+b_1*a_2+b_2\)。两个士兵信息的合并可以这样处理,可以发现,士兵合并后的参数是跟输入\(x\)无关的。
现在我们再加一个士兵3号,如果我们先合并1号和2号,输出为\((a_1*a_2*x+b_1*a_2+b_2)*a_3+b_3=a_1*a_2*a_3*x+b_1*a_2*a_3+b_2*a_3+b_3\)。
如果我们先合并2号和3号,输出为\(a_2*a_3*(a_1*x+b_1)+b_2*a_3+b_3=a_1*a_2*a_3*x+b_1*a_2*a_3+b_2*a_3+b_3\)。
两者是相等的,所以这种合并符合结合律。
然后,我们就可以愉快地用线段树维护区间信息辣!!
代码
#include <cstdio>
#include <cstdlib>
#define maxn (int)(5 * 1e5 + 10)
#define mod 998244353
#define ll long long
using namespace std;
struct op {
ll multi, plus;
op operator+(op x) {
op ans;
ans.multi = multi * x.multi;
ans.plus = plus * x.multi + x.plus;
ans.multi %= mod;
ans.plus %= mod;
return ans;
}
} p[maxn];
struct node {
op val;
int l, r;
} tree[maxn << 2];
void update(int x) { tree[x].val = tree[x << 1].val + tree[x << 1 | 1].val; }
void build(int x, int left, int right) {
int mid = left + right >> 1;
tree[x].l = left;
tree[x].r = right;
if (left == right) {
tree[x].val = p[left];
return;
}
build(x << 1, left, mid);
build(x << 1 | 1, mid + 1, right);
update(x);
return;
}
void modify(int pos, int x, op k) {
if (tree[pos].l == tree[pos].r) {
tree[pos].val = k;
return;
}
int mid = tree[pos].l + tree[pos].r >> 1;
if (x <= mid)
modify(pos << 1, x, k);
else
modify(pos << 1 | 1, x, k);
update(pos);
return;
}
op query(int pos, int left, int right) {
op ans1, ans2;
ans1.multi = ans2.multi = 1;
ans1.plus = ans2.plus = 0;
if (tree[pos].l >= left && tree[pos].r <= right)
return tree[pos].val;
int mid = tree[pos].l + tree[pos].r >> 1;
if (left <= mid)
ans1 = query(pos << 1, left, right);
if (right > mid)
ans2 = query(pos << 1 | 1, left, right);
return ans1 + ans2;
}
int main() {
int n, q, tp, x, i, y, z, w;
op temp;
ll ans;
scanf("%d%d", &n, &q);
for (i = 1; i <= n; i++) {
scanf("%d%d", &tp, &x);
if (tp) {
p[i].plus = x;
p[i].multi = 1;
} else {
p[i].plus = 0;
p[i].multi = x;
}
}
build(1, 1, n);
for (i = 1; i <= q; i++) {
scanf("%d%d%d%d", &w, &x, &y, &z);
if (!w) {
if (y) {
temp.multi = 1;
temp.plus = z;
} else {
temp.multi = z;
temp.plus = 0;
}
modify(1, x, temp);
} else {
temp = query(1, x, y);
ans = z * temp.multi % mod + temp.plus;
ans %= mod;
printf("%lld\n", ans);
}
}
return 0;
}