首页 > 代码库 > BZOJ 1798: [Ahoi2009]Seq 维护序列seq (线段树乘法加法的混合操作)
BZOJ 1798: [Ahoi2009]Seq 维护序列seq (线段树乘法加法的混合操作)
题目:点击打开链接
大意:一个数组,三个操作,第一种是区间[a,b]每个数乘乘,第二种是区间[a,b]每个数加c,第三种是查询[a,b]区间的和并对p取摸。
两种操作就不能简单的只往下传标记。每次传乘法标记时,要把加法标记同时乘上乘法标记,例如某个区间先进来一个加法标记add,之后又进来一个乘法标记mul。
那么结果为(x + add) * mul = x * mul + add * mul。这样向下传标记的时候就相对独立。递归边界更新加法标记之前先乘上该节点的mul,左右儿子down的时候先将儿子的add乘上本节点的mul。
最后说一下sum,比如本节点的存在加法标记x和乘法标记y,并且是先加上x,再乘上y,则左儿子的sum要更新为(sum+x)*y。由于乘法标记传到本节点的时候更新了加法标记,x = x*y,所以sum[o<<1] = (左区间的长度*x) + sum[o<<1]*y。
/************************************************************** Problem: 1798 User: __ElemenT Language: C++ Result: Accepted Time:4676 ms Memory:10184 kb ****************************************************************/ #include <cstdio> #define lson o<<1, l, m #define rson o<<1|1, m+1, r typedef long long LL; const int maxn = 100005; int n, a, b, c, k, q; LL sum[maxn<<2], add[maxn<<2], mul[maxn<<2], p; void up(int o) { sum[o] = (sum[o<<1]+sum[o<<1|1]) %p; } void build(int o, int l, int r) { add[o] = 0, mul[o] = 1; if(l == r) { scanf("%lld", &sum[o]); return; } int m = (l+r) >> 1; build(lson); build(rson); up(o); } void down(int o, int len) { // if(add[o] != 0 && mul[o] != 1) { add[o<<1] = (add[o<<1] * mul[o] + add[o]) %p; add[o<<1|1] = (add[o<<1|1] * mul[o] + add[o]) %p; mul[o<<1] = mul[o<<1] * mul[o] %p; mul[o<<1|1] = mul[o<<1|1] * mul[o] %p; sum[o<<1] = (sum[o<<1] * mul[o] + add[o] * (len-(len>>1))) %p; sum[o<<1|1] = (sum[o<<1|1] * mul[o] + add[o] * (len>>1)) %p; add[o] = 0, mul[o] = 1; // } } void update(int o, int l, int r, int op) { if(a <= l && r <= b) { if(op == 1) { add[o] = add[o]*c %p; mul[o] = mul[o]*c %p; sum[o] = sum[o]*c %p; } else { add[o] = (add[o] + c) %p; sum[o] = (sum[o] + (LL)c*(r-l+1)) %p; } return; } down(o, r-l+1); int m = (l+r) >> 1; if(a <= m) update(lson, op); if(m < b ) update(rson, op); up(o); } LL query(int o, int l, int r) { if(a <= l && r <= b) return sum[o] %p; down(o, r-l+1); int m = (l+r) >> 1; LL ans = 0; if(a <= m) ans = query(lson); if(m < b ) ans += query(rson); return ans %p; } int main() { scanf("%d%lld", &n, &p); build(1, 1, n); scanf("%d", &q); while(q--) { scanf("%d%d%d", &k, &a, &b); if(k != 3) { scanf("%d", &c); update(1, 1, n, k); } else printf("%lld\n", query(1, 1, n)); } return 0; }
BZOJ 1798: [Ahoi2009]Seq 维护序列seq (线段树乘法加法的混合操作)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。