题目背景
大芳有一个不太好的习惯:在车里养青蛙。青蛙在一个n厘米(11n毫米s)的Van♂杆子上跳来跳去。她时常盯着青蛙看,以至于突然逆行不得不开始躲交叉弹。有一天他突发奇想,在杆子上每1厘米为一个单位,瞎涂上了墨水,并且使用mOgic,使青蛙跳过之处墨水浓度增加x。当然,他还会闲着无聊滴几滴墨水再涂♂抹均匀。
他现在无时无刻都想知道,第l厘米到第r厘米墨水的浓度是多少?
哦不!等等,他现在找到了一个计算器,可以输入几个数字与x,计算他们的x次幂和,所以。。。他想知道的是第l厘米到第r厘米墨水的浓度的x次幂和是多少?
题目描述
大芳有3种舰长技能骚操作
-
续:把青蛙放到第l厘米处,戳青蛙使其跳至r。效果:第l厘米至第r厘米墨水浓度增加x
- 抚♂摸:擦干杆子某一部分,重新滴加墨水并抹匀。效果:使第l厘米至第r厘米墨水浓度都变成x
最后一种是:
- 压线逆行,将车流看做⑨弹幕找安定点,掏出计算器,大喊板载后计算:
第l厘米至第r厘米墨水浓度的x次幂和是几何?记得答案要
模1000000007
输入输出格式
输入格式:
第一行n和m,表示杆子长n厘米,大芳要进行m次骚操作。
第二行n个数字,表示初始墨水浓度。第i个数字为第i厘米墨水浓度
接下来每行4个数字,依次为:操作编号(1、2或3),l,r,x
输出格式:
每次进行3操作,输出一行表示答案
记得膜模1000000007
输入输出样例
5 5 19844 14611 26475 4488 6967 2 1 3 15627 2 1 2 30113 2 3 5 14686 2 5 5 32623 3 1 2 8
466266421
说明
分析:比较好的一道线段树的题,在细节处理方面收获了很多.
暴力可以拿到30分,如果会一点线段树的基本操作应该是可以拿到60分的,想要拿满分关键在于k的处理.
我们保存每个区间的i次幂和,如果是区间覆盖就很好办,幂*区间长度;如果是区间加就有点麻烦,要用到二项式定理来展开.比较麻烦,手推一下就出来了.同时要打两个标记,一个是覆盖标记,一个是累加标记,覆盖标记要在累加标记之前判断,并且会使累加标记变成0.
如果在pushdown中写向子区间的更改操作代码就会比较繁琐,为了精简代码,我们可以另写一个函数来下传标记和更改子区间,这是一个非常实用的技巧.
本题的模数比较大,如果直接上int的话一般而言是不会有问题的,但是当两数相乘的时候可能会爆int,所以临时转换成long long然后取模.
我以前是喜欢把不同的量放在不同的数组里写,今天用了一下struct,感觉放在结构体里写会更加直观工整.犯了一个最脑残的错误:逻辑判断弄错了,每次在操作1的时候我都会输出一次,导致过了样例然而所有点全WA.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 200010, mod = 1000000007; int n, m,k = 10,c[20][20],a[maxn]; struct node { int l, r,v,ad,st; int p[11]; }e[maxn << 1]; void pushup(int o) { for (int i = 0; i <= k; i++) e[o].p[i] = (e[o * 2].p[i] + e[o * 2 + 1].p[i]) % mod; } void sett(int o, int v) { int res = 1; for (int i = 0; i <= k; i++) { e[o].p[i] = 1LL*res*(e[o].r - e[o].l + 1) % mod; res = 1LL*res * v % mod; } e[o].ad = 0; e[o].st = v; } void addd(int o, int v) { for (int i = k; i >= 0; i--) { int res = 1, t = 0; for (int j = i; j >= 0; j--) { t = (t + 1LL * e[o].p[j] *c[i][j] % mod * res) % mod; res = 1LL * res * v % mod; } e[o].p[i] = t; } e[o].ad = (e[o].ad + v) % mod; } void pushdown(int o) { if (e[o].st != -1) { sett(o * 2, e[o].st); sett(o * 2 + 1, e[o].st); e[o].st = -1; } if (e[o].ad) { addd(o * 2, e[o].ad); addd(o * 2 + 1, e[o].ad); e[o].ad = 0; } } void build(int l, int r, int o) { e[o].l = l; e[o].r = r; e[o].st = -1; e[o].ad = 0; if (l == r) { e[o].p[0] = 1; for (int i = 1; i <= k; i++) e[o].p[i] = 1LL * e[o].p[i - 1] * a[l] % mod; return; } int mid = (l + r) >> 1; build(l, mid, o * 2); build(mid + 1, r, o * 2 + 1); pushup(o); } void add(int l, int r, int o, int x, int y, int v) { if (x <= l && r <= y) { addd(o,v); return; } pushdown(o); int mid = (l + r) >> 1; if (x <= mid) add(l, mid, o * 2, x, y, v); if (y > mid) add(mid + 1, r, o * 2 + 1, x, y, v); pushup(o); } void update(int l, int r, int o, int x, int y, int v) { if (x <= l && r <= y) { sett(o, v); return; } pushdown(o); int mid = (l + r) >> 1; if (x <= mid) update(l, mid, o * 2, x, y, v); if (y > mid) update(mid + 1, r, o * 2 + 1, x, y, v); pushup(o); } int query(int l, int r, int o, int x, int y, int v) { if (x <= l && r <= y) return e[o].p[v]; pushdown(o); int mid = (l + r) >> 1, sum = 0; if (x <= mid) sum = (sum + query(l, mid, o * 2, x, y, v)) % mod; if (y > mid) sum = (sum + query(mid + 1, r, o * 2 + 1, x, y, v)) % mod; return sum; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); build(1, n, 1); c[0][0] = 1; for (int i = 1; i <= k; i++) { c[i][0] = 1; for (int j = 1; j <= i; j++) c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod; } for (int i = 1; i <= m; i++) { int op, l, r, x; scanf("%d%d%d%d", &op, &l, &r, &x); if (op == 1) add(1, n, 1, l, r, x); if (op == 2) update(1, n, 1, l, r, x); if (op == 3) printf("%d ", query(1, n, 1, l, r, x)); } return 0; }