碼谷國慶遊記Day1-Day3

北大大佬wdyhy爲我們上了前三天的課
下面是總結

ay3)

Day1

講課內容——基礎算法

一、前綴和

1.一維前綴和:

  • sum[i]=sum[i-1]+a[i];

2.二維前綴和:

  • sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]
  • 令b[i][j] = Σa[i][k] (k<=j)
    sum[i][j]=sum[i-1][j]+b[i][j];
    ↑對於第一維把第二維的前綴和求出來,然後再把第一維的前綴和加起來就是二維前綴和了
二、差分

1.一維差分:

  • 一維數組b[i]=a[i]-a[i-1],我們發現對b[i]求前綴和之後就是數組a.
    sum[i]=b[1]+b[2]+…b[i]
    =a[1]-a[0]+a[2]-a[1]+…a[i]-a[i-1]
    =a[i]
  • 二維差分
    多組詢問,如何對於一個二維數組求出一個矩陣(x1,y1,x2,y2)中所有元素的和.
    sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1].
T1&T2【差分】

【題目描述】
給你一個長爲n的序列,有兩種操作,一種是讀入(l,r,x)表示對於區間[l,r]中每個數的值加上x,另一種是查詢[l,r]內的數的和是多少.
n<=100000,第一種操作個數<=100000,第二種操作個數<=100

【思路】
對序列差分,我們發現一次操作對差分序列的影響只有b[l],b[r+1],那麼就在b[l]+x,在b[r+1]-x即可.(b[l]=(a[l]+x)-a[l-1]=原b[l]+x; b[r+1]同理可證)

又∵第二種操作個數很少,∴第二種操作每次暴力求出前綴和來即可.

STL庫一些函數的使用:

三、堆
  • 堆在stl庫中是優先隊列,他維護的是一個單調遞增或單調遞減的數列.可以滿足每次插入一個數,查詢堆頂(最大/最小數),刪除堆頂.

  • priority_queue q;表示定義一個大根堆.堆中的元素從大到小排序.
    priority_queue<int,vector,greater >q;表示小根堆
    q.push(x);把x加入到堆中.
    q.pop();彈出堆頂
    q.empty()表示判斷q是否空了.
    q.size();表示q的大小.

T3合併果子【堆】

【題目描述】
你有n堆果子,每次你可以合併任意兩堆果子,消耗的體力值是這兩堆果子個數和,問你最小消耗多少體力.

【思路】
每次可以貪心合併最小的兩堆果子,這樣一定是最優的,因爲你一定會合並n-1次,這樣保證每次合併的兩堆果子一定最小

用小根堆維護即可,即每次彈出最小的兩個果子,然後合併起來再放到堆裏.

T3中位數LuoguP1168【堆】

【題目描述】
給出一個長度爲N的非負整數序列Ai,對於所有1≤k≤(N+1)/2,輸出A1, A3, …, A2k-1的中位數。即前1,3,5,…個數的中位數。
n<=100000
【思路】
先考慮求1~n的中位數,把數組排序最中間的數就是答案.考慮怎麼用堆來維護這個答案.

對於一個長爲i的序列,其中i爲奇數.
記x表示這個序列的中位數,然後我們用大根堆維護前一半小的數,用小根堆維護後一半小的數,這樣序列分爲了[1,(i-1)/2]∪[(i+1)/2,i]

現在考慮加進去一個數w
如果w<=x那麼w要放到[1,(i-1)/2]中,同時因爲加入了w,相應的就要把x放到[(i+1)/2,i]中,然後把[1,(i-1)/2]中的堆頂作爲x.
如果w>x相反.
也就是我們要時刻維護[1,(i-1)/2]和[(i+1)/2,i]這兩個堆大小相同.

T5序列合併LuoguP1631【堆】

【題目描述】
給你兩個大小爲n的序列A和B,把這兩個數列中的數兩兩相加,得到n2個數,問這n2個數中前n小的是多少.
n<=100000

【思路】
把A和B排序然後維護兩個指針p1,p2.
一開始p1=p2=1,然後維護一個struct(p1,p2,a[p1]+b[p2]).
其中對於第三關鍵字a[p1]+b[p2]維護小根堆.
每次彈出堆頂(p1,p2,a[p1]+b[p2]),然後再把(p1+1,p2,a[p1+1]+b[p2])和(p1,p2+1,a[p1]+b[p2+1])加入到堆中.
當我們彈出了n次就結束即可.
因爲只會彈出n次,每次彈出只會新增加兩個數,所以堆中只會有O(n)級別的數.
複雜度nlogn.

四、map
  • map就是一個映射.
    對於一個數x,我想知道它的映射f(x)是多少,但是x可能是一個10^9級別的數f(x)開不下.
  • map中使用的內存是你向map中添加的元素個數.
    map<long long,int> mp.
    表示mp[(ll)x]=(int)數組下標可以是long long級別的數,表示的數是int類型的.
  • map常常在字符串哈希中用到.

洛谷大佬關於map文章

五、set
  • set實際上是顆平衡樹.,維護的是維護元素遞減的數據結構
    支持加入一個數,刪除任意一個數,查詢最小值,查詢最大值,查詢數x是否存在…
    set維護的是一個集合,加入有兩個x,在set中其實只有一個x.
  • set st;
    st.insert(x); 插入x
    st.erase(x); 刪除x
    st.begin(); 返回的是最小值的指針.前面加就是最小值即(st.begin());
    st.rbegin(); 返回的是最大值的指針

洛谷大佬關於set文章

六、vector
  • vector相當於鄰接表,但是不開o2會比鄰接錶慢一點,但是vector很好用.
  • vector a[n];
    表示對於每個a[i]是一個鄰接表,鄰接表中的元素是int類型的.
  • vec.push_back(x)表示將元素x加入到vec中,vec下標是從0開始的.
    vec.size()表示的是vec的大小.
    vec.clear();表示清空vec
    vector比鄰接表好的在於它可以排序.
    sort(vector.begin(),vector.end(),cmp);
七、單調棧
  • 棧是一個先進後出的數據結構.
    單調棧就是維護單調性的東西.
T6模板【單調棧】

【題目描述】
給你一個數組a[n],求對於每一個i,在1~i-1中離i最近的比i大的數是多少.沒有輸出0.
n<=100000

【思路】
單調棧維護一個遞減的序列.
a[st[1]]>=a[st[2]]>=a[st[3]]>=…a[st[t]].
當我們枚舉i時如果a[i]>=a[st[t]],就把t–,直到一個a[st[t]]>a[i],這個時候st[t]就是大於a[i]離i最近的數.

八、單調隊列
  • 隊列是一個先進先出的數據結構.
    單調隊列就是維護單調性的隊列.
T6滑動窗口LuoguP1886【單調隊列】

【題目描述】
給你一個數組a[n],對於每個i,求[i-k+1,i]中的最大值和最小值.

【思路】
最大值:單調隊列在加入元素的時候是和單調棧一樣的.
考慮維護最大值,那麼就用單調隊列維護一個遞減的序列,a[q[h]]>=a[q[h+1]]>=…>=a[q[t]]那麼最大值就是單調隊列的隊頭的數.
因爲有一個[i-k+1,i]的限制,所以單調隊列要維護隊頭,而單調棧不用

怎麼處理[i-k+1,i]呢?
while (q[h]<i-k+1) h++;
因爲單調隊列維護的序列單調遞減,而維護的下表是單調遞增的,所以每次暴力彈出即可.
同樣最小值的話就用單調隊列維護一個單調遞增的序列即可.

T8理想正方形LuoguP2216

【題目描述】
有一個hw的整數組成的矩陣,現請你從中找出一個nn的正方形區域,使得該區域所有數中的最大值和最小值的差最小.
h,w<=1000

【思路】
怎麼去動態維護一個矩陣的最大值和最小值呢?
先考慮最大值,同樣最小值是一樣的.
考慮把nn的矩陣看成n個1n的矩陣,然後我們把這n個1*n的小矩陣的最大值求出來得到了a1…an.
也就是我們把二維矩陣拆成了一維.

問題轉化爲了我們要維護[i-n+1,i]中的最大值和最小值.
這樣我們枚舉起點i,從(1,i)~(i+n-1,n)這個矩陣開始從上往下每次增加一個大小爲1n的矩陣,同時彈出後面一個1n的矩陣即可.
複雜度n^2.

T9bzoj3831【dp+單調隊列】

【題目描述】
有一排n棵樹,第i棵樹的高度是d[i],MHY要從第一棵樹到第n棵樹去找他的妹子玩.
如果MHY在第i棵樹,那麼他可以跳到第i+1,i+2,…,i+k棵樹.
如果MHY跳到一棵不矮於當前樹的樹,那麼他的勞累值會+1,否則不會.
爲了有體力和妹子玩,MHY要最小化勞累值。
n<=10^6

【思路】
設dp[i]表示到i的最小花費.可以把dp轉移看作這樣
dp[i]=min(dp[j]+1) (i-k≤j<i)
dp[i]=min(dp[j]) (i-k≤j<i&&h[j]>h[i])

我們發現一個特殊性質,每次花費的體力只有1.
因此維護一個單調隊列,使得隊列中的dp[i]從小到大遞增,dp值相同的樹高度選更大的.
因爲跳到更高的樹代價只有1,因此我們只要看單調隊列中dp最小值能否直接跳過來即可.如果直接跳不過來那麼選次小值一定不優.

九、並查集
  • 並查集就是維護聯通性的東西.
  • 路徑壓縮並查集是O(n)的
    路徑不壓縮並查集是O(n^2)的
  • 路徑不壓縮並查集可以看做將連通性轉化爲一棵樹,如果每個點一次次往上跳到根節點是O(n)的
    路徑壓縮並查集可以看做一個菊花圖,每個點跳到根是O(1)的.
T10LuoguP2078【並查集】

【題目描述】
在某個城市裏,住着 n 頭豬,他們經常搶東西喫,任何兩頭豬要麼是朋友,要麼是敵人,並 且滿足 1,我的敵人的敵人就是朋友.2,我的朋友的朋友還是朋友.他們分爲了好多豬圈,在 同一個豬圈裏的都是朋友.問這個城市裏有幾個豬圈.
n<=100000

【思路】
並查集模板題. 考慮對每個豬 i,建立虛點 i+n. 如果兩個人 x,y 是朋友那麼就用並查集把 x 和 y 連接起來. 如果兩個人 x,y 不是朋友,那麼就用並查集把 x 和 y+n,y 和 x+n 連接起來. 最後求出有多少個聯通塊即可.

T11LuoguP1196【並查集】

【題目描述】
初始有n個獨立的艦船,有T組操作,有兩種操作Mi,j表示將i艦船所在的艦隊接到j艦隊尾部,Ci,j表示詢問i,j艦船是否在同一艦隊,如果在,輸出i,j之間隔了幾個艦船.
n<=10^5
T<=5*10^5

【思路】
設f[i]表示i所在聯通塊編號,cnt[i]表示i所在聯通塊大小,up[i]表示i到i所在艦隊的頭部一共有幾個艦船,那麼對於查詢x,y之間的艦船就是up[x]-up[y]-1個.
在路徑壓縮時維護up數組,up[k]+=up[f[k]]即可.

T12AtcoderE【並查集】

【題目描述】
一個n個點的樹,每條邊有邊權,要求對於每個點i,從i點到其他的n-1個點的距離爲兩個點間邊權最小值,求每個點i到其他n-1個點的距離之和.
n<=100000

【思路】
按照邊權從大到小排序然後加入到樹中,每加入一條邊一定連接了兩個聯通塊x和y,記每個聯通塊大小爲sz[i],那麼對於x聯通塊中每個點的答案增加了sz[y]*邊權,y中每個點增加了sz[x]*邊權.
用並查集維護聯通塊,同時啓發式合併在並查集上打標記即可.

並查集標記考慮在啓發式合併時下傳給每個點,合併後再打一個標記表示一個聯通塊中每個點要增加多少權值.

十、倍增
  • 倍增其實就是相當於二進制拆分,所以倍增的時間複雜度是log的.

LCA

  • lca,就是指樹上兩點x,y的最近公共祖先.
    對於樹上兩個點x,y,假設x的深度比y大,那麼就先讓x跳到和y深度相同的祖先上,然後x和y深度相同同時向上跳,直到跳到一個點x和y相遇了爲止.

  • 設f[i][j]表示i點向上跳2^j步跳到哪個點.
    如何處理f數組?

  • 注意:2的j次方+2的j次方=2^(j+1).
    那麼f[i][j]數組就可以遞推了.
    對於每個i,假設現在已經處理了f[i][0~j-1]的值,那麼只要能更新f[i][j]那麼就可以遞推了.
    一開始f[i][0]表示i的父親.
    f[i][j]=f[f[i][j-1]][j-1]
    表示從i跳2^(j-1) 到f[i][j-1],然後又跳了2^(j-1)到了f[f[i][j-1]][j-1].

  • 考慮我們現在要從x開始向上跳d步,因爲我們f[i][j]表示的是跳2^j步上去的,所以要通過跳很多次湊出d步來.
    有一個性質是每個數都可以被若干個不同的2的冪加起來表示.
    設d=2a1+2a2…
    那麼我們考慮貪心,從高到低枚舉二進制位,假設當前在點x,如果2的j次方<=d,那麼就令x向上跳2的j次方,並且d-=2^j.

  • 那麼求lca就可以在log的複雜度裏做了,假設x的深度比y大,那麼x就要向上先跳dep[x]-dep[y]步,現在dep[x]=dep[y].
    如果x=y那麼lca就是x
    如果x!=y

  • 我們令x和y同時向上跳,在跳的時候從大到小枚舉二進制位j表示跳2的j次方,如果x和y同時跳2的j次方步後跳到一起了,那麼就不跳(因爲可能跳到lca跳過了),否則就讓x和y同時跳2^j步,這樣他們跳到最後的時候就差一步就可以跳到lca了.
    最後再讓x和y跳一步就是lca了.

  • 倍增相關數組和倍增數組的轉移方式都是類似的.
    比如w[i][j]表示從i向上跳2^j步經過邊的最小值是多少.
    w[i][j]=min(w[i][j-1],w[f[i][j-1]][j-1]).

T13 poj1679【倍增】

【題目描述】
n個點,m條邊的圖,每條邊有邊權,求這棵最小生成樹是否是唯一的,如果存在另一棵樹和這棵樹權值相同那麼說是不唯一的.
n<=100000

【思路】
先建出最小生成樹來,然後枚舉不在最小生成樹上的邊(x,y,w),然後找到最小生成樹上x到y點之間邊權最大值,如果最大值=w,那麼說明將w替換最大值後形成樹權值相同,最小生成樹就不是唯一的了.

顯然儘可能走限重大的邊更優.
那麼就考慮建最大生成樹,問題轉換爲了求兩點間限重最小值.
同樣維護從i向上2^j步邊權最小值即可.

T15JosephLand

【題目描述】
有n個點組成的樹,每條邊(x,y)表示x到y有一條長度爲1的邊,然後有m個售票站形如(x,w,d),每個售票站在點x,你可以花w的錢買一張票向上最多走d步.
問每個點到根最少花多少元.

【思路】
n^2dp?
設dp[i]表示從i點到根的最小花費,dp[1]=0
我們從1開始向下轉移,保證在每個點x的dp值更新之前他的祖先的dp都被更新過了.
轉移?

假如i號點有一個售票站,票價爲w,向上跳d步,那麼轉移就是
dp[i]=min(dp[j]+1)其中j是i的祖先並且dep[i]-dep[j]<=d.
暴力就是從i枚舉向上d步的每一個祖先然後更新dp[i].

如何考慮用倍增來優化?
記錄w[i][j]表示i點向上跳2^j步的dp值的最小值.
那麼對於每個d可以拆成若干個2的冪加起來,也就是可以在log的時間複雜度內通過w[i][j]來優化這個dp了.
時間複雜度nlogn.

十一、倍增RMQ
  • 支持nlogn預處理,o(1)回答區間最小值/最大值的數據結構.

  • 原理就是倍增.
    設f[i][j]表示區間[i,i+2^j-1]這個區間的最小值,轉移也很顯然
    更新f[i][j]的時候假設已經更新過f[i][0~j-1]了.
    f[i][j]=min(f[i][j-1],f[i+2^(j-1)][j-1]).
    這樣就可以在nlogn複雜度預處理了.

  • 如何O(logn)查詢?
    考慮和倍增lca一樣,可以把這個區間拼成若干個大小爲2的冪的區間.
    這樣對於這log個區間來更新答案即可.

  • 如何O(1)查詢?
    假設區間[l,r],長度爲d=r-l+1,我們找到最大的j滿足2^j<=d.
    我們用兩個區間拼起來可以得到[l,r]
    [l,l+2j-1]和[r-2j+1,r]這兩個區間一定覆蓋了[l,r]這個區間.
    那麼答案就是min(f[l][j],f[r-2^j+1][j]);

  • 注意rmq只能維護區間最大值和最小值,因爲它是通過兩個區間覆蓋得到答案的.

T16bzoj4569

【題目描述】
一個長度爲n的大數,用S1S2S3…Sn表示,其中Si表示數的第i位,S1是數的最高位,告訴你一些限制條件,每個條件表示爲四個數,l1,r1,l2,r2,即兩個長度相同的區間,表示子串Sl1Sl1+1Sl1+2…Sr1與Sl2Sl2+1Sl2+2…Sr2完全相同。比如n=6時,某限制條件l1=1,r1=3,l2=4,r2=6,那麼123123,351351均滿足條件,但是12012,131141不滿足條件,前者數的長度不爲6,後者第二位與第五位不同。問滿足以上所有條件的數有多少個。
注意:不能有前導0

【思路】
n^2暴力?
nlogn?
我們把條件所限制的相等的位置看做一個連通塊,那麼最後只要知道有幾個連通塊就可以求出答案來了。
所以一個暴力的做法就是對於每個限制,貪心地把兩個區間對應的數通過並查集合並,最後查詢多少個連通塊即可.
時間複雜度n^2.

solution:
設f[i][j]表示從i開始2j區間和誰對應相同,f[i][j]=f[k][j]=p那麼從i開始2j個字符和從k開始2^j個字符是相同的.並且這兩個區間都屬於並查集p.
考慮對於每個限制
將區間[l,r]分爲[l,l+2d-1]和[r-2d+1,r]然後將這兩個區間通過並查集合並.
最後處理完所有操作,對於所有的f[i][j]將f[i][j-1],f[i+2^(j-1)][j-1]所對應的並查集分別合併即可.

十二、二分
  • 當我們遇到某些最大值最小或最小值最大之類的問題就可以考慮二分答案.
T17營救LuoguP1396【二分答案】

【題目描述】
該市有m條大道連接n個區,一條大道將兩個區相連接,每個大道有一個擁擠度。小明的媽媽雖然很着急,但是不願意擁擠的人潮衝亂了她優雅的步伐。所以請你幫她規劃一條從s至t的路線,使得經過道路的擁擠度最大值最小。

【思路】
使最大值最小顯然要先二分答案,是一個左開右閉區間的二分形式.
然後用邊權小於等於二分值mid的邊看是否能使s到t聯通即可.

T18往奧格瑞瑪的道路LuoguP1462【二分答案】

【題目描述】
有n個城市,有m條邊,每條雙向邊連接xi,yi,當hly走過這個點時他會掉wi滴血,初始hly有b滴血,hly想從1走到n,每個城市有一個ci表示經過這個城市收費ci,現在hly希望能從1到n在自己不掛掉的情況下經過的城市中收費的最大值最小.
n<=10^5.

【思路】
顯然符合二分性,那麼二分答案mid,將收費小於等於mid的點加入到圖中,然後跑最短路,如果最短路長度小於等於b那麼mid就合法.

十三、貪心
T19國王遊戲LuoguP1080【貪心】

【題目描述】
有n個人,國王在最前面,n個大臣兩隻手上分別有一個數,每個大臣獎賞爲排在該大臣前面的所有人的左手上的數的乘積除以他自己右手上的數,然後向下取整得到的結果.
求一種站隊的順序滿足獎賞最多的大臣獎賞儘可能少.

【思路】
假設有兩個人x,y,在他們之前所有人的左手上數乘積爲s.
x在y前的收益爲max(s/xb,sxa/yb)
x在y後的收益爲max(s/yb,s
ya/xb)
因爲sxa/yb一定大於s/yb,sya/xb一定大於s/xb.
所以只要sxa/yb<sya/xb那麼x就在y前面.
也就是按照xaxb<yayb排序即可.

設dp[i]表示1~i的最長上升子序列
轉移很顯然dp[i]=max(dp[j]+1)(a[j]<a[i])
時間複雜度n^2

T20AtcoderD - Forest【並查集+貪心】

【題目描述】
有n個點,m條邊形成了n-m個樹的森林,每個點有權值val[i],給兩個點x,y連邊的代價爲val[x]+val[y],且用完一次x,y就不能再用x,y了.問最少多少代價能夠將這n個點連成一棵樹.
n,m<=100000

【思路】
先用並查集求出有cnt個聯通塊,也就是我們要連cnt-1條邊,每次連兩個點,所以要選出2*(cnt-1)個點,並且每個聯通塊都要連至少一次邊.
那麼我們就先選出每個聯通塊中最小的點,再從這cnt個聯通塊中選擇2*(cnt-1)-cnt個點即可.
怎麼維護?

我們發現每次選完一個點之後要把這個點刪掉,並且每次找到所有聯通塊中最小的點並且刪掉.
對於每個聯通塊維護一個小根堆加入聯通塊中所有點,然後維護一個全局的小根堆加入所有聯通塊中最小的點,每次選全局中的最小值然後把相應的聯通塊中的點也刪掉即可.

T21Cover the Tree

【題目描述】
給你一顆大小爲n的樹,求讓你用最少的簡單路徑使得能夠覆蓋整棵樹所有邊.輸出方案
n<=10^5.

【思路】
樹的重心:樹上一個點滿足其子樹大小最大值最小.
樹的重心性質:它的每個子樹大小都<=n/2,因爲如果有一個子樹大小>n/2,那麼可以讓重心向這棵子樹偏移,這樣不斷調整重心.

找到重心我們發現這棵樹被分爲好多個<=n/2的子樹,那麼就可以進行貪心了.
如何通過重心的性質來進行貪心?

考慮把每個子樹中的葉子結點放到不同的vector中,開一個堆,每次選擇vector最大的兩個子樹中的兩個葉子結點並覆蓋相應的路徑,然後把這兩個葉子結點從相應的子樹中去掉即可.

————————————Day1 End————————————

Day2

模擬賽

講課內容——圖論

Day3

模擬賽

爆零的一天

講課內容——動態規劃&雜題選講

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章