UOJ 228 基礎資料結構練習題
阿新 • • 發佈:2019-02-18
線段樹
多寫點騙點訪問量
加法好說,開平方就很氣了,因為開平方的資訊不能合併。這樣就導致常規的線段樹,分塊之類的維護數列和的方法不能用。我們只能另闢蹊徑,找尋性質。
我們發現開平方操作會讓數字減少得很快,大概六七次就能把一個數變成1。這給我一種在不特意構造資料的情況下,最終數字很容易變得大部分都一樣的感覺。
如果沒有加法,那隻要判斷一下區間裡有沒有非1的,有就暴力做,這樣複雜度沒問題。考慮有加法,加法會讓數字又變得不一樣,仔細觀察我們發現對於區間修改[l,r],l和r處相鄰數字差變大,也就是隻有兩個地方發生變化。
那我們用線段樹維護序列。如果區間裡數字都一樣就按常規線段樹的來做,否則遞迴進左右子樹。不考慮遞迴進子樹的話,時間複雜度是 (mlogn)
有一種神奇的資料: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();
}