題意 題目鏈接 Sol 傳說中的吉司機線段樹??感覺和BZOJ冒險那題差不多,就是強行剪枝。。。 這題最坑的地方在於對於操作1,$C >= 0$, 操作2中需要對0取max,$a[i] >= 0$,這不就是統計最小值出現的次數麽?? 按照套路 維護好區間賦值標記 / 區間加法標記 / 區間max標記 ...
題意
Sol
傳說中的吉司機線段樹??感覺和BZOJ冒險那題差不多,就是強行剪枝。。。
這題最坑的地方在於對於操作1,$C >= 0$, 操作2中需要對0取max,$a[i] >= 0$,這不就是統計最小值出現的次數麽??
按照套路
維護好區間賦值標記 / 區間加法標記 / 區間max標記 / 區間最小值 / 區間最小值出現的次數 / 區間次小值
對於第二個操作就拆成區間加 和 區間max
區間max是一個很神奇的操作
設當前加入的數為val
若val>=mn,那該操作對該區間無影響
若se < val < mn,該操作只會對次小值產生影響,因為對其他的標記均不會產生影響,因此打一個額外的標記即可
否則暴力遞歸
時間複雜度:$O(n log^2n)$??
另外這東西可以做區間加 / 查詢歷史版本,前者維護一下標記就行,,後者嘛,,,等長大了再研究吧qwq
#include<cstdio> #include<algorithm> #define LL long long //#define int long long using namespace std; const int MAXN = 3 * 1e5 + 10; const LL INF = 1e10 +10; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } #define ls k << 1 #define rs k << 1 | 1 int N, Q; int a[MAXN]; struct Node { int l, r, siz, cnt; LL Mx, add, mn, se, cov; }T[MAXN << 2]; void update(int k) { //T[k].mn = min(T[ls].mn, T[rs].mn); if(T[ls].mn < T[rs].mn) T[k].cnt = T[ls].cnt, T[k].mn = T[ls].mn, T[k].se = min(T[rs].mn, T[ls].se); else if(T[ls].mn > T[rs].mn) T[k].cnt = T[rs].cnt, T[k].mn = T[rs].mn, T[k].se = min(T[ls].mn, T[rs].se); else T[k].cnt = T[ls].cnt + T[rs].cnt, T[k].mn = T[ls].mn, T[k].se = min(T[ls].se, T[rs].se); } void MemP(int k, LL val) { T[k].cov = val; T[k].cnt = T[k].siz; T[k].se = INF; T[k].Mx = -INF; T[k].add = 0; T[k].mn = val; } void AddP(int k, LL val) { if(T[k].se != INF) T[k].se += val; if(T[k].Mx != -INF) T[k].Mx += val; T[k].mn += val; T[k].add += val; } void MaxP(int k, LL val) { T[k].mn = max(T[k].mn, val);// T[k].Mx = max(T[k].Mx, val); } void pushdown(int k) { if(T[k].cov != INF) MemP(ls, T[k].cov), MemP(rs, T[k].cov), T[k].cov = INF; if(T[k].add) AddP(ls, T[k].add), AddP(rs, T[k].add), T[k].add = 0; if(T[k].Mx != -INF) MaxP(ls, T[k].Mx), MaxP(rs, T[k].Mx), T[k].Mx = -INF; } void Build(int k, int ll, int rr) { T[k] = (Node) {ll, rr, rr - ll + 1, 0, -INF, 0, 0, INF, INF}; if(ll == rr) { T[k].mn = a[ll]; T[k].cnt = 1; return ; } int mid = T[k].l + T[k].r >> 1; Build(ls, ll, mid); Build(rs, mid + 1, rr); update(k); } void IntMem(int k, int ll, int rr, LL val) { if(ll <= T[k].l && T[k].r <= rr) {MemP(k, val); return ;} pushdown(k); int mid = T[k].l + T[k].r >> 1; if(ll <= mid) IntMem(ls, ll, rr, val); if(rr > mid) IntMem(rs, ll, rr, val); update(k); } void IntAdd(int k, int ll, int rr, LL val) { if(ll <= T[k].l && T[k].r <= rr) {AddP(k, val); return ;} pushdown(k); int mid = T[k].l + T[k].r >> 1; if(ll <= mid) IntAdd(ls, ll, rr, val); if(rr > mid) IntAdd(rs, ll, rr, val); update(k); } void IntMax(int k, int ll, int rr, LL val) { if(val <= T[k].mn) return ; if(ll <= T[k].l && T[k].r <= rr && T[k].se > val) {//tag MaxP(k, val); return ; } int mid = T[k].l + T[k].r >> 1; pushdown(k); if(ll <= mid) IntMax(ls, ll, rr, val); if(rr > mid) IntMax(rs, ll, rr, val); update(k); } LL Query(int k, int ll, int rr) { // int ans = 0; if(ll <= T[k].l && T[k].r <= rr) return (T[k].mn == 0 ? T[k].cnt : 0); pushdown(k); int mid = T[k].l + T[k].r >> 1; if(ll > mid) return Query(rs, ll, rr); else if(rr <= mid) return Query(ls, ll, rr); else return Query(ls, ll, rr) + Query(rs, ll, rr); } main() { // freopen("4355.in", "r", stdin); // freopen("4355.out", "w", stdout); N = read(); Q = read(); for(int i = 1; i <= N; i++) a[i] = read(); Build(1, 1, N); while(Q--) { int opt = read(), l = read(), r = read(), val; if(opt == 3) printf("%d\n", Query(1, l, r)); else { val = read(); if(opt == 1) IntMem(1, l, r, val); else { IntAdd(1, l, r, val); IntMax(1, l, r, 0); } } } return 0; }