日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NOIP2017 列队——动态开点线段树

發(fā)布時間:2025/3/17 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NOIP2017 列队——动态开点线段树 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Description:

Sylvia 是一個熱愛學(xué)習(xí)的女♂孩子。

前段時間,Sylvia 參加了學(xué)校的軍訓(xùn)。眾所周知,軍訓(xùn)的時候需要站方陣。

Sylvia 所在的方陣中有n×m名學(xué)生,方陣的行數(shù)為?n,列數(shù)為?m。

為了便于管理,教官在訓(xùn)練開始時,按照從前到后,從左到右的順序給方陣中 的學(xué)生從 1 到?n×m?編上了號碼(參見后面的樣例)。即:初始時,第?i?行第?j?列 的學(xué)生的編號是(i?1)×m+j。

然而在練習(xí)方陣的時候,經(jīng)常會有學(xué)生因為各種各樣的事情需要離隊。在一天 中,一共發(fā)生了?q件這樣的離隊事件。每一次離隊事件可以用數(shù)對(x,y)(1xn,1ym)描述,表示第?x?行第?y?列的學(xué)生離隊。

在有學(xué)生離隊后,隊伍中出現(xiàn)了一個空位。為了隊伍的整齊,教官會依次下達 這樣的兩條指令:

  • 向左看齊。這時第一列保持不動,所有學(xué)生向左填補空缺。不難發(fā)現(xiàn)在這條 指令之后,空位在第?x?行第?m?列。

  • 向前看齊。這時第一行保持不動,所有學(xué)生向前填補空缺。不難發(fā)現(xiàn)在這條 指令之后,空位在第?n?行第?m?列。
  • 教官規(guī)定不能有兩個或更多學(xué)生同時離隊。即在前一個離隊的學(xué)生歸隊之后, 下一個學(xué)生才能離隊。因此在每一個離隊的學(xué)生要歸隊時,隊伍中有且僅有第?n?行 第?m?列一個空位,這時這個學(xué)生會自然地填補到這個位置。

    因為站方陣真的很無聊,所以 Sylvia 想要計算每一次離隊事件中,離隊的同學(xué) 的編號是多少。

    注意:每一個同學(xué)的編號不會隨著離隊事件的發(fā)生而改變,在發(fā)生離隊事件后 方陣中同學(xué)的編號可能是亂序的。

    Hint

    Solution

    一年前暴力敲了30pts

    一年后暴力敲了60pts

    沒什么長進啊

    還是不會正解。

    ?

    1.不懂樹狀數(shù)組

    2.不想寫平衡樹

    所以我們寫動態(tài)開點線段樹

    ?

    首先發(fā)現(xiàn),對于x=1的點,可以想到對這個鏈開一棵長度為max(n,m)+q的線段樹。每次找第k個有數(shù)的地方,然后放到最后的位置。

    發(fā)現(xiàn),每次向前對齊只有最后一列要動,

    向左看齊,只是當(dāng)前的行會向左移動。

    所以,為了便于操作,我們開n+1棵線段樹,前n棵維護i行,1~m-1的答案

    最后一棵n+1,維護最后一列n個答案。

    ?

    然后我們就得到了一個優(yōu)秀的MLE做法辣!~~

    所以就要動態(tài)開點線段樹。

    ?

    (因為我比較弱)所以簡單講解一下動態(tài)開點線段樹。

    發(fā)現(xiàn),有的時候,線段樹需要維護的區(qū)間很大很大,但是實際用到的節(jié)點很少很少。

    那么,我們干脆就不要開這么多的節(jié)點,用到的時候再向內(nèi)存要。

    也就是說,我們建立了一棵殘疾的線段樹,缺少很多枝葉,但是絕對夠用了。

    畫個圖大概理解一下(雖然也不太對)

    實心邊框的點都是我們申請內(nèi)存給的,虛的點是沒用的。就算申請也不用,實在是浪費資源。

    所以,

    我們開局只有一個根,裝備葉子全靠給。

    例如我們要建立一個權(quán)值線段樹,但是在線操作不讓你離散化,值域又是inf級別的,

    像這樣,即使這個區(qū)間的范圍很大,但是如果詢問q比較少的話,我們只需要qloginf個節(jié)點,就可以辦到。

    ?

    (發(fā)現(xiàn)和主席樹有點像,但是省空間的思想還是有些不同的。)

    ?

    然后我們用動態(tài)開點線段樹來做這個題。

    線段樹根節(jié)點維護的區(qū)間是max(n,m)+q;

    開始每個線段樹甚至連根也不用建,需要的時候會建起來。

    每個線段樹節(jié)點記錄sz,子樹實際的人數(shù)大小。(開始的時候,只有1~n(m-1)是sz=r-l+1的)

    sz可以用一個函數(shù)處理。雖然并沒有這么多的葉子,但是實際上,建出這么多的葉子,也是這個sz(這也是能動態(tài)開點的條件)

    再記錄一個val(long long型需注意),記錄當(dāng)前節(jié)點所代表的人的編號

    這個編號val只有在葉子節(jié)點才有用。

    ?

    其實每次詢問引起的變化是:樹x的第y個人走了,進入了樹n+1的末尾,樹n+1的第x走了,進入樹x的末尾。

    每次詢問,如果y==m就進入線段樹n+1查詢,否則進入線段樹x查詢,找到答案ans輸出

    查詢的時候,順便sz--,刪掉途經(jīng)點的sz(就不用pushup了)

    把ans這個編號放進n+1線段樹的末尾(新開一個位置)

    同樣,途經(jīng)sz++

    如果y!=m說明,第x棵線段樹最后進來一個人。就把n+1的第x個人查詢(刪除),放進線段樹x的末尾(新開一個位置)。

    這樣子,其實每棵線段樹根節(jié)點的sz都保持為m-1(或n)

    ?

    Code

    #include<bits/stdc++.h> #define mid ((l+r)>>1) using namespace std; typedef long long ll; const int N=3e5+5; const int M=1e7+2; ll n,m,q; struct node{int ls,rs;int sz;ll val; }t[M]; int id,tot; int rt[N]; ll now; int cur[N]; int up; int get(int l,int r){if(now==n+1){if(r<=n) return r-l+1;if(l<=n) return n-l+1;return 0;}if(r<m) return r-l+1;if(l<m) return m-l;return 0; } ll query(int &x,int l,int r,int c){if(!x){x=++tot;t[x].sz=get(l,r);if(l==r){if(now==n+1) t[x].val=l*m;else t[x].val=(now-1)*m+l;}}t[x].sz--;if(l==r) return t[x].val;if((!t[x].ls&&c<=get(l,mid))||c<=t[t[x].ls].sz) return query(t[x].ls,l,mid,c);else{if(!t[x].ls) c-=get(l,mid);else c-=t[t[x].ls].sz;return query(t[x].rs,mid+1,r,c);} } void upda(int &x,int l,int r,int to,ll d){if(!x){x=++tot;t[x].sz=get(l,r);if(l==r){t[x].val=d;}}t[x].sz++;if(l==r) return;if(to<=mid) return upda(t[x].ls,l,mid,to,d);else return upda(t[x].rs,mid+1,r,to,d); } int main() {scanf("%lld%lld%lld",&n,&m,&q);int x,y;ll ans;up=max(n,m)+q;while(q--){scanf("%d%d",&x,&y);if(y==m) now=n+1,ans=query(rt[now],1,up,x);else now=x,ans=query(rt[now],1,up,y);printf("%lld\n",ans);now=n+1;upda(rt[now],1,up,n+(++cur[now]),ans);if(y!=m){now=n+1;ans=query(rt[now],1,up,x);now=x;upda(rt[now],1,up,m-1+(++cur[now]),ans);}}return 0; }

    ?

    ?

    upda:2018.11.2

    感覺這個動態(tài)開點線段樹其實不算是典型的動態(tài)開點23333

    一般的線段樹都是區(qū)間表示連續(xù)一些下標(biāo)之類。動態(tài)開點也是如此。

    但是這個做法的話,愣是把線段樹寫成了平衡樹的存儲方式。

    區(qū)間的長度僅僅代表的是預(yù)留空間。

    就是把許多點壓成了一個點。

    ?

    轉(zhuǎn)載于:https://www.cnblogs.com/Miracevin/p/9582412.html

    新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎!定制產(chǎn)品紅包拿不停!

    總結(jié)

    以上是生活随笔為你收集整理的NOIP2017 列队——动态开点线段树的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。