1. 程式人生 > >可修改主席樹

可修改主席樹

前面講完了主席樹,那現在就來考慮可修改的主席樹。
如果直接修改主席樹,我們就需要用O(nlog2n)的時間來逐個逐個修改,那麼我們可否用更小的時間來修改呢?
我們之所以前面的主席樹的修改時間如此大是因為每個rooti的主席樹包含了root1,root2...rooti1的主席樹。
那麼可不可以用另外一種神奇的東西來使得rooti只用管理少數幾個root,這樣的東西似乎似曾相識。
線段樹!!!
rooti的主席樹包含了rooti×2rooti×2+1的主席樹。
我是用樹狀陣列來實現的,每次的修改就沿著x向其父親走,每到一個root修改一次,而詢問操作就把所有合起來管理詢問的區間的r

oot取出來,遞迴求第k大時就所有root同時跳到其兒子或父親,然後就可以完美解決了,空間複雜度O(nlog2n),時間複雜度O(nlog2n)

下面附一下程式碼():

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<numeric>
#include<queue>
#include<functional>
#include<set>
#include<map>

#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--) #define MAXN 100010 typedef double LL; using namespace std; struct node{ LL v1,v2,v3; int v4; int l,r,tot; }tree[4000010]; int root[1010],tot,h[MAXN],f[11],k,n,m; LL ans; int get(){ char ch; while (ch=getchar(),(ch<'0'||ch>'9')&& ch!='-'
); char c=ch; int s; if (c!='-')s=c-48;else s=0; while (ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-48; return c=='-'?-s:s; } void inse(int l,int r,int &t,int x,int y,int tim){ if (!t)t=++tot; tree[t].tot+=tim; if (l==r){ if (x>y){ tree[t].v1+=LL(1)/(x-y)*tim; tree[t].v2+=LL(y)/(x-y)*tim; tree[t].v3+=LL(y*y)/(x-y)*tim; } tree[t].v4+=(x+y)*tim; return; } int mid=(l+r)/2; if (x<=mid)inse(l,mid,tree[t].l,x,y,tim); else inse(mid+1,r,tree[t].r,x,y,tim); int ls=tree[t].l,rs=tree[t].r; tree[t].v1=tree[ls].v1+tree[rs].v1; tree[t].v2=tree[ls].v2+tree[rs].v2; tree[t].v3=tree[ls].v3+tree[rs].v3; tree[t].v4=tree[ls].v4+tree[rs].v4; } void add(int x,int y,int tim){ int y1=y; while (y<1001){ inse(1,1001,root[y],x,y1,tim); y=y+(y& -y); } } void cc(int i,int x){ if (i<n){ if (h[i]>h[i+1])add(h[i],h[i+1],-1); else add(h[i+1],h[i],-1); if (x<h[i+1])add(h[i+1],x,1); else add(x,h[i+1],1); } if (i>1){ if (h[i-1]>h[i])add(h[i-1],h[i],-1); else add(h[i],h[i-1],-1); if (x<h[i-1])add(h[i-1],x,1); else add(x,h[i-1],1); } h[i]=x; } void getl(){ fo(i,1,k)f[i]=tree[f[i]].l; } void getr(){ fo(i,1,k)f[i]=tree[f[i]].r; } void getanswer(int l,int r,int x){ if (l>x){ fo(i,1,k)ans=ans+0.5*(tree[f[i]].v1*x*x-tree[f[i]].v2*x*2+tree[f[i]].v3); return; } if (r<=x){ fo(i,1,k)ans=ans+tree[f[i]].tot*x-0.5*tree[f[i]].v4; return; } int tt[11]; fo(i,1,k)tt[i]=f[i]; getl(); int mid=(l+r)/2; getanswer(l,mid,x); fo(i,1,k)f[i]=tt[i]; getr(); getanswer(mid+1,r,x); } void getans(int x){ k=0; int y=x; while (y){ f[++k]=root[y]; y=y-(y & -y); } ans=0; getanswer(1,1001,x); } int main(){ n=get();m=get(); fo(i,1,n)h[i]=get()+1; fo(i,1,n-1) if (h[i]<h[i+1])add(h[i+1],h[i],1); else add(h[i],h[i+1],1); fo(i,1,m){ char ch; while (ch=getchar(),ch!='Q'&&ch!='U'); if (ch=='Q'){ int x=get()+1; getans(x); printf("%.3lf\n",ans); } else{ int x=get()+1,y=get()+1; cc(x,y); } } }

相關推薦

修改主席

前面講完了主席樹,那現在就來考慮可修改的主席樹。 如果直接修改主席樹,我們就需要用O(nlog2n)的時間來逐個逐個修改,那麼我們可否用更小的時間來修改呢? 我們之所以前面的主席樹的修改時間如此大是因為每個rooti的主席樹包含了root1,root2...

少年,想學帶修改主席嗎 | BZOJ1901 帶修改區間第k小

== write algo i++ sin esp 天下 read 一個 少年,想學帶修改主席樹嗎 | BZOJ1901 帶修改區間第k小 有一道題(BZOJ 1901)是這樣的:n個數,m個詢問,詢問有兩種:修改某個數/詢問區間第k小。 不帶修改的區間第k小用主席樹很好

[BZOJ 4826]影魔 區間修改主席 標記永久化

brush sca ostream www cpp pad stream i+1 return 為了這道題還特地去學了標記永久化,可能對於區間修改主席樹或者樹套樹比較有用吧OvO 我們可以把答案分為兩部分:p1造成的和p2造成的 我們枚舉序列,用單調棧求出序列每一個位置

修改主席狀數組+主席

scanf pos spa end tor cal pan name gin 終於學了這個我仰慕已久的算法。 對於待修改的主席樹我們只需要多開一維,進行修改後的求和。復雜度進化為O(nlog^2n) 我們需要開R0 L0兩個數組記錄樹狀數組的“路徑” 然後其他操作就和主席樹

hdu 4348 - 區間修改主席

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=4348   題目連結: 時間倒流那肯定是主席樹啊,然後區間修改要用永久標記.下傳標記不能使用,空間會炸.   #include <iostream>

[學習筆記]帶修改主席

1、Dynamic Rankings 區間帶修改的第 \(k\) 大需要用帶修改主席樹。 如果用平常的主席樹的效率是多少呢? 查詢 \(O(logn)\),暴力修改 \(O(nlogn)\),時間不支援 那麼就需要平衡一下兩者的時間複雜度 我們用樹狀陣列套主席樹,每次查詢把 \(logn\) 個 \

2112 動態單點修改主席

題目連結 題意:n個數,q個詢問 (n<=50000, q<=10000) Q x y z 代表詢問[x, y]區間裡的第z小的數 C x y    代表將(從左往右數)第x個數變成y 對於更新, 我們不改變這些已經建好的樹, 而是另建一批樹S

不帶修改主席模板

對於一部分線段樹看似無法直接做的題,可以用主席樹來做。 主席樹就是對每個字首開一棵線段樹,當然,直接這樣會MLE。 可以使用一種類似動態開節點的方法可以有效避免MLE。 具體可以參考我的部落格,那

HDU 2665.Kth number-無修改區間第K小-持久化線段(主席)模板

sort ota nbsp ani show 去重 第k小 math urn Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth

持久化線段主席)模板

spa std nod d+ sin 整理 ostream pan int 比賽時候寫的,這裏整理到這裏 #include <iostream> #include <cstdio> #include <cstring> using

BZOJ 3674 持久化並查集加強版(主席變形)

als ret desc scan sync scanf ops 只需要 ica 3673: 可持久化並查集 by zky Time Limit: 5 Sec Memory Limit: 128 MB Submit: 2515 Solved: 1107 [

不帶修改主席

main ring ostream algorithm return else scan ons root #include<map> #include<stdio.h> #include<string.h> #include<v

主席持久化線段版)

可持久化線段樹 else init 修改 update 懶惰標記 logs scan amp 求區間和模板 1 struct node{ 2 int l[maxn*20],r[maxn*20]; //區間大小 maxn = 1e5時為20倍,不夠就開4

[poj2104]持久化線段入門題(主席

unique tor oot 入門題 個數 索引 方便 return 出現的次數 解題關鍵:離線求區間第k小,主席樹的經典裸題; 對主席樹的理解:主席樹維護的是一段序列中某個數字出現的次數,所以需要預先離散化,最好使用vector的erase和unique函數,很方便;如

【模板】持久化線段 1(主席

base math 一次 數據 mar 指定 das min 第k小 題目背景 這是個非常經典的主席樹入門題——靜態區間第K小 數據已經過加強,請使用主席樹。同時請註意常數優化 題目描述 如題,給定N個正整數構成的序列,將對於指定的閉區間

[Luogu] 持久化線段 1(主席

tdi ace oid root post space out 節點 nod https://www.luogu.org/problemnew/show/P3834 #include<cstdio> #include<iostream> #

【刷題】洛谷 P3834 【模板】持久化線段 1(主席

!= tchar 這樣的 信息 reg har mem hair define 題目背景 這是個非常經典的主席樹入門題——靜態區間第K小 數據已經過加強,請使用主席樹。同時請註意常數優化 題目描述 如題,給定N個正整數構成的序列,將對於指定的閉區間查詢其區間內的第K小值。

持久化線段主席

AS string can -a 過程 思想 oot and amp 關於可持久化線段樹 可持久化線段樹可以將歷史版本的線段樹記憶下來,並支持新建版本與查詢歷史版本。 其中有一道經典的靜態主席樹的題就是求區間k大。 建樹的過程其實就是先建一棵空樹,再按輸入順序插入節點。需要

HDU 4348 To the moon(主席區間修改

歷史 ons 證明 spa 產生 sin Go != 註意 題意 給你一個區間,支持如下操作: 在一段區間內加上一個值,並生成一個歷史版本 查詢某個版本下一段區間內的和 回到一個歷史版本上並舍棄之後的版本 做法 這就是主席樹區間修改裸題啦QwQ 上一篇博客我講了主席樹可

ZOJ -2112 Dynamic Rankings 主席修改的區間第K大

OS alt \n tar txt push fopen amp get Dynamic Rankings 帶修改的區間第K大其實就是先和靜態區間第K大的操作一樣。先建立一顆主席樹, 然後再在樹狀數組的每一個節點開線段樹(其實也是主席樹,共用節點), 每次修改的時候都按照