LibreOJ 6278 數列分塊入門 2(分塊區間加法,二分)
阿新 • • 發佈:2020-07-22
解題思路
詢問區間小於某個數個個數顯然可以用二分來做,但是如果配合上區間加法就有些複雜了。即使對每個區間排序,用標記來代替修改,但是對於邊緣的資料來說,需要暴力修改,而暴力修改後打破區間的有序性。那就暴力修改之後再重新排序(沒錯,就是這麼狠(笑
程式碼
const int maxn = 1e5+10; struct INFO { int num, i; INFO(int a=0, int b=0): num(a), i(b) {} bool operator < (const INFO &a) const { return num < a.num; } } arr[maxn]; int n,sz,num,l[maxn],r[maxn],bl[maxn],lazy[maxn]; void build() { num = (n+sz-1)/sz; for (int i = 1; i<=num; ++i) { l[i] = (i-1)*sz+1, r[i] = i*sz; } r[num] = n; for (int i = 1; i<=n; ++i) bl[i] = (i-1)/sz+1; for (int i = 1; i<=num; ++i) sort(arr+l[i],arr+r[i]+1); } void update(int a, int b, int c) { if (bl[a]==bl[b]) { for (int i = l[bl[a]]; i<=r[bl[a]]; ++i) if (arr[i].i>=a && arr[i].i<=b) arr[i].num += c; sort(arr+l[bl[a]], arr+r[bl[a]]+1); return; } for (int i = bl[a]+1; i<bl[b]; ++i) lazy[i] += c; for (int i = l[bl[a]]; i<=r[bl[a]]; ++i) if (arr[i].i>=a) arr[i].num += c; for (int i = l[bl[b]]; i<=r[bl[b]]; ++i) if (arr[i].i<=b) arr[i].num += c; sort(arr+l[bl[a]], arr+r[bl[a]]+1); sort(arr+l[bl[b]], arr+r[bl[b]]+1); } int find(int a, int b, int c) { int ans = 0; if (bl[a]==bl[b]) { for (int i = l[bl[a]]; i<=r[bl[a]]; ++i) if (arr[i].i>=a && arr[i].i<=b && arr[i].num+lazy[bl[i]]<c) ++ans; return ans; } for (int i = bl[a]+1; i<bl[b]; ++i) ans += lower_bound(arr+l[i],arr+r[i]+1,INFO(c-lazy[i],0))-arr-l[i]; for (int i = l[bl[a]]; i<=r[bl[a]]; ++i) if (arr[i].i>=a && arr[i].num+lazy[bl[i]]<c) ++ans; for (int i = l[bl[b]]; i<=r[bl[b]]; ++i) if (arr[i].i<=b && arr[i].num+lazy[bl[i]]<c) ++ans; return ans; } int main() { scanf("%d",&n); for (int i = 1; i<=n; ++i) { scanf("%d",&arr[i].num); arr[i].i = i; } sz = sqrt(n); build(); for (int i = 1,op,l,r,c; i<=n; ++i) { scanf("%d%d%d%d",&op,&l,&r,&c); if (!op) update(l,r,c); else printf("%d\n",find(l,r,c*c)); } return 0; }