1. 程式人生 > >UOJ 228 基礎資料結構練習題

UOJ 228 基礎資料結構練習題

線段樹

多寫點騙點訪問量

加法好說,開平方就很氣了,因為開平方的資訊不能合併。這樣就導致常規的線段樹,分塊之類的維護數列和的方法不能用。我們只能另闢蹊徑,找尋性質。

我們發現開平方操作會讓數字減少得很快,大概六七次就能把一個數變成1。這給我一種在不特意構造資料的情況下,最終數字很容易變得大部分都一樣的感覺。

如果沒有加法,那隻要判斷一下區間裡有沒有非1的,有就暴力做,這樣複雜度沒問題。考慮有加法,加法會讓數字又變得不一樣,仔細觀察我們發現對於區間修改[l,r],l和r處相鄰數字差變大,也就是隻有兩個地方發生變化。

那我們用線段樹維護序列。如果區間裡數字都一樣就按常規線段樹的來做,否則遞迴進左右子樹。不考慮遞迴進子樹的話,時間複雜度是O

(mlogn)。考慮遞迴進去,當且僅當有一次修改的端點碰到了這個節點,最多m個修改,其實也就是最多有O(mlogn)個結點會這樣。遞迴進去的操作相當於回收標記,最多需要7次就可以回收完畢,因此總O(7nlogn)

有一種神奇的資料:343434,開方完是121212,加2後又是343434,這樣的特判一下- -

這種基於均攤時間複雜度的線段樹好神奇啊……適用的範圍大概是一些操作會改變序列的平衡性,另一些操作會恢復序列的平衡性的題目吧……

#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 100005
using namespace std; namespace runzhe2000 { typedef long long ll; typedef double db; int n, m, a[N]; struct seg { ll sum, mx, mi, tag; }t[N*5]; void pushup(int x) { t[x].sum = t[x<<1].sum + t[x<<1|1].sum; t[x].mx = max(t[x<<1].mx, t[x<<1
|1].mx); t[x].mi = min(t[x<<1].mi, t[x<<1|1].mi); } void pushdown(int x, int l, int r) { if(!t[x].tag) return; int mid = (l+r)>>1; ll &tag = t[x].tag; t[x<<1].tag += tag; t[x<<1|1].tag += tag; t[x<<1].sum += tag * (mid - l + 1); t[x<<1|1].sum += tag * (r - mid); t[x<<1].mx += tag; t[x<<1|1].mx += tag; t[x<<1].mi += tag; t[x<<1|1].mi += tag; tag = 0; } void build(int x, int l, int r) { if(l == r){t[x].sum = t[x].mx = t[x].mi = a[l]; t[x].tag = 0; return;} int mid = (l+r)>>1; build(x<<1,l,mid); build(x<<1|1,mid+1,r); pushup(x); } void add(int x, int l, int r, int ql, int qr, int v) { if(ql <= l && r <= qr){t[x].tag += v; t[x].sum += (ll)(r-l+1) * v; t[x].mi += v; t[x].mx += v; return;} pushdown(x,l,r); int mid = (l+r)>>1; if(ql <= mid) add(x<<1,l,mid,ql,qr,v); if(mid < qr ) add(x<<1|1,mid+1,r,ql,qr,v); pushup(x); } void modi(int x, int l, int r, int ql, int qr) { if(ql <= l && r <= qr && t[x].mx - t[x].mi == (int) sqrt((db)t[x].mx) - (int) sqrt((db)t[x].mi)) { ll v = (ll)sqrt((double)t[x].mx) - t[x].mx; t[x].tag += v; t[x].sum += (ll)(r-l+1) * v; t[x].mi += v; t[x].mx += v; return; } pushdown(x,l,r); int mid = (l+r)>>1; if(ql <= mid) modi(x<<1,l,mid,ql,qr); if(mid < qr ) modi(x<<1|1,mid+1,r,ql,qr); pushup(x); } ll query(int x, int l, int r, int ql, int qr) { if(ql <= l && r <= qr) return t[x].sum; int mid = (l+r)>>1; ll ret = 0; pushdown(x,l,r); if(ql <= mid) ret += query(x<<1,l,mid,ql,qr); if(mid < qr) ret += query(x<<1|1,mid+1,r,ql,qr); return ret; } void main() { scanf("%d%d",&n,&m); for(int i = 1; i <= n; i++) scanf("%d",&a[i]); build(1,1,n); for(int opt, l, r, x; m--; ) { scanf("%d",&opt); if(opt == 1) // add { scanf("%d%d%d",&l,&r,&x); add(1,1,n,l,r,x); } else if(opt == 2) // sqrt { scanf("%d%d",&l,&r); modi(1,1,n,l,r); } else { scanf("%d%d",&l,&r); printf("%lld\n",query(1,1,n,l,r)); } } } } int main() { runzhe2000::main(); }