noip模擬賽 大芳的逆行板載
題目背景
大芳有一個不太好的習慣:在車裏養青蛙。青蛙在一個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
輸入輸出樣例
輸入樣例#1: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\n", query(1, n, 1, l, r, x)); } return 0; }
noip模擬賽 大芳的逆行板載