P1438 無聊的數列
阿新 • • 發佈:2022-04-15
感謝所有AC
傳送門
反思
題目思考量小,此題解關鍵在於反思。
不管任何題目都需要在思路已經無法拓展開了才能參考題解!儘可能地保持25分鐘以上的思考(尤其是這種藍綠以上的題目)。
思路
等差數列很容易引出差分的思路。對於一組等差數列,例如 1 3 5 7 9 來說,其差分陣列為 1 2 2 2 2 (-9) ,也就是說等差數列的差分陣列是 a d d ... d ,a 為首項,d 為公差。那麼題目中的數列可以在差分的狀態下進行區間處理,即對於單點 L 來說,需要加上 a ,對於區間 L+1 到 R 需要加上 d ,對於區間 R+1 要減去末項。如何詢問單點值?由差分的性質,可知還原陣列可以對差分陣列進行字首和,字首和不就是我們線段樹所維護的東西?對於詢問 p ,直接訪問區間 1 到 p 就可以實現單點查詢。
程式碼
#include<iostream> #define maxn 100007 using namespace std; typedef long long ll; int n, m, l, r, p, opt, a[maxn], ch[maxn], K, D; ll t[maxn << 2], lzy[maxn << 2]; void pushup(int u) { t[u] = t[u << 1] + t[(u << 1) + 1]; } void build(int u, int l, int r) { if (l == r) t[u] = ch[l]; else { int m = l + r >> 1; build(u << 1, l, m), build((u << 1) + 1, m + 1, r); pushup(u); } } void maketag(int u, int len, ll x) { lzy[u] += x; t[u] += (ll)len * x; } void pushdown(int u, int l, int r) { int m = l + r >> 1; maketag(u << 1, m + 1 - l, lzy[u]); maketag((u << 1) + 1, r - m, lzy[u]); lzy[u] = 0; } void update(int u, int l, int r, int L, int R, ll x) { if (L <= l && r <= R) maketag(u, r - l + 1, x); else if (!(l > R || r < L)) { pushdown(u, l, r); int m = l + r >> 1; update(u << 1, l, m, L, R, x); update((u << 1) + 1, m + 1, r, L, R, x); pushup(u); } } ll query(int u, int l, int r, int L, int R) { if (L <= l && r <= R) return t[u]; else if (!(l > R || r < L)) { pushdown(u, l, r); int m = l + r >> 1; return query(u << 1, l, m, L, R) + query((u << 1) + 1, m + 1, r, L, R); } else return 0; } void deal(int l, int r, int K, int D) { update(1, 1, n, l, l, K); if (l != r) update(1, 1, n, l + 1, r, D); if (r + 1 > n) return; update(1, 1, n, r + 1, r + 1, -((ll)K + (ll)(r - l) * D)); } int main(void) { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) ch[i] = a[i] - a[i - 1]; build(1, 1, n); while (m--) { cin >> opt; if (opt == 1) { cin >> l >> r >> K >> D; deal(l, r, K, D); } else { cin >> p; ll ans = query(1, 1, n, 1, p); cout << ans << '\n'; } } return 0; }