權值線段樹 動態開點(學習小結)

2021-08-21 13:28:11 字數 3567 閱讀 9542

#include

#include

#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)

const

int n=200050;

using

namespace

std;

int t[n*18],ls[n*18],rs[n*18],x,now,add,tot;

int tag[n*18];

int n,nowmin,rt;

char s[2];

void r(int &n)

void push(int p)

}void maintain(int p)

void ins(int l,int r,int x,int &p)

push(p);int m=l+r>>1;

if (x<=m) ins(l,m,x,ls[p]);

else ins(m+1,r,x,rs[p]);

maintain(p);

}void update(int l,int r,int x,int y,int &p)

push(p);int m=l+r>>1;

if (x<=m)update(l,m,x,y,ls[p]);

if (m1,r,x,y,rs[p]);

maintain(p);

}int query(int l,int r,int x,int &p)

int main()

; if (s[1]=='a');

if (s[1]=='s');

if (s[1]=='f')

if (x>t[1])puts("-1");

else

printf("%d\n",query(-n,n,t[1]-x+1,rt)+add);

}printf("%d",now-t[1]);

return

0;}

「我希望能使用更多的魔法。不對,是預定能使用啦。最終我要被大家稱呼為大魔法使。為此我決定不惜一切努力。」

——《the grimoire of marisa》霧雨魔理沙

魔理沙一如既往地去帕秋莉的大圖書館去借魔導書(grimoire) 來學習魔道。

最開始的時候,魔理沙只是一本一本地進行研究。然而在符卡戰中,魔理沙還是戰不過帕秋莉。

好在魔理沙對自己的借還和研究結果進行了記錄,從而發現了那些魔導書的精妙之處。

帕秋莉的那些魔導書,每本都有乙個類別編號ti 和威力大小pi。而想要獲得最有威力的魔法,就必須同時研究一些魔導書。而研究的這些魔導書就必須要滿足,類別編號為t 的書的本數小於等於t,並且總共的本數小於等於乙個給定的數n。而研究這些魔導書之後習得的魔法的威力就是被研究的魔導書的威力之和。

為了擊敗帕秋莉,魔理沙想要利用自己發現的規律來獲得最有威力的魔法。

她列出了計畫中之後m 次的借還事件,並想要知道每個事件之後自己所能獲得的魔法的最大威力。可她忙於魔法材料——蘑菇的收集,於是這個問題就交給你來解決了。

輸入檔案grimoire.in。

第1 行2 個整數n,m,分別表示魔理沙能研究的魔導書本數的上限和她的借還事件數。

之後m 行,每行的形式為「op t p」(不含引號)。op 為「borrow」 或「return」,分別表示借書和還書。t 為乙個整數,表示這本書的類別編號。p為乙個整數,表示這本書的威力大小。注意,還書時如果有多本書滿足類別編號為t,威力大小為p,這表明這些書都是相同的,魔理沙會任選其中一本書還回去。如果你問我為何會有相同的書,多半因為這是魔導書吧。

輸出檔案grimoire.out。

一共m 行,每行乙個整數,即每個事件之後的最大威力。

5 10

borrow 1 5811

borrow 3 5032

return 3 5032

borrow 3 5550

borrow 5 3486

return 1 5811

return 3 5550

borrow 4 5116

borrow 3 9563

borrow 5 94

5811

10843

5811

11361

14847

9036

3486

8602

18165

18259

對於5% 的資料,1 <= t,n,m <= 50。

對於10% 的資料,1 <= t,n,m <= 100。

對於30% 的資料,1 <= t,n,m<= 10 000。

另有30% 的資料,1 <= p <= 1 000。

對於100% 的資料,1 <= t,n,m <= 300 000,1<= p<= 1 000 000 000。

另外,總共有30% 的資料,滿足沒有「return」 操作。這部分資料均勻分布。

轉換一下問題模型,其實就是我們有很多棵小樹,然後第i棵的前i個數,又可以放到乙個大樹中,然後我們要維護大樹中前n大的數的和,那就很簡單了,我們借一本書的時候,我們判斷這本書的威力值是否大於對應的小樹中的第i個,如果是,則刪除在大樹中的原來小樹中的第i數,加入這個數到大樹中,然後無論如何都是要將這個數加入到小樹中的,刪除類似。

#include

#include

#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)

const

int m=1000000000;

const

int n=300000+55;

typedef

long

long ll;

using

namespace

std;

int t[n*90],ls[n*90],rs[n*90],rt[n*90],tot,n,m;

char s[7];

ll c[n*90];

void r(int &n)

void ins(int l,int r,int x,int &p,int w)

int m=l+r>>1;

if (x<=m) ins(l,m,x,ls[p],w);

else ins(m+1,r,x,rs[p],w);

t[p]=t[ls[p]]+t[rs[p]];

c[p]=c[ls[p]]+c[rs[p]];

}int query(int l,int r,int x,int &p)

ll sum(int l,int r,int n,int &p)

int main()

else

else

ins(1,m,p,rt[z],1);}}

else

else

else

ins(1,m,p,rt[z],-1);}}

if (t[rt[0]]<=n) printf("%lld\n",c[rt[0]]);

else

printf("%lld\n",sum(1,m,n,rt[0]));

}return

0;}

權值線段樹 動態開點

普通平衡樹 題目給的資料是1e 7到1e7,直接寫線段樹記憶體肯定是比較吃力,而且題目還要維護rank和第k大,這時候就用到動態開點了,因為運算元一共就1e5,所以最多也只需要開 log 2 2e7 大小的陣列。修改函式void add int rt,int l,int r,int x,int v ...

權值線段樹小結

線段樹大概地球人都知道了,就是以陣列的下表建立線段樹來進行一些區間操作,這裡介紹一下權值線段樹,顧名思義,其實權值線段樹也是線段樹的一種。一 權值線段樹線段樹與簡單線段樹的區別就像他的名字一樣,他的葉子節點存的並不是陣列的下表,而是陣列中數的權值,這種操作很簡單的解決一些問題。二.例題分析 1.求逆...

線段樹動態開點

為了降低權值線段樹的空間複雜度,可以不直接建出整棵線段樹的結構,而是在最初只建立乙個根節點,當需要訪問某棵為建立的子樹的時候,再建立代表這個子樹的節點。動態開點的線段樹用變數記錄左右節點的編號。值域為1 n的動態開點線段樹在m次單點修改後,節點規模為o mlogn 例題 p1908 逆序對 這題n最...