1. 程式人生 > >2017多校Round4(hdu6067~hdu6079)

2017多校Round4(hdu6067~hdu6079)

分解 分割線 集合 明顯 註意細節 nlog 裏的 線段樹 情況

補題進度:10/13

1001

待填坑

1002(kmp+遞推)

題意:

有長度為n(<=50000)的字符串S和長度為m(m<=100)的字符串T,有k(k<=50000)組詢問,每個詢問(L,R),表示對於所有的(i,j)(1<=i<=L,R<=j<=n),將S[1..i]和S[j..n]拼接起來,求這個新的S‘中出現T的次數總和

分析:

我們分析對於一個(i,j)的情況下,那麽T的出現有三種情況,一種是整體在[1..i],第二種是整體在[j..n],第三種是部分拼接起來

我們先考慮前兩種怎麽求

只需要將S和T去kmp,記錄下匹配成功的位置,pre[i]表示S[i-m+1]..S[i]和T是否匹配成功,suf[i]表示S[i]..S[i+m-1]和T是否匹配成功

我們對pre[i]求個前綴和,對suf[i]求個後綴和,那麽pre[i]就表示左分割線在i的時候左邊有多少個完整的T,suf[i]表示右分割線在i的時候右邊有多少個完整的T

但註意我們的一個詢問[L,R]並不是固定分割線L和R,而是左分割線<=L,右分割線>=R

那麽就是L左邊所有分割線的答案去乘上右邊分割線的個數 + R右邊所有分割線的答案去乘上左邊分割線的個數

所以只要對pre[i]和suf[i]再去求前綴和以及後綴和

接下來考慮跨區間的問題

因為m很小,所以對於每個詢問我們可以枚舉一個k,表示左邊是T[1..k] 右邊是T[k+1..m]

preh[i]表示S[i-k+1]..S[i]和T[1..k]是否匹配成功,sufh[i]表示S[i]..S[i+m-k-1]和T[k+1..m]是否匹配成功

那麽答案只要最後加上左邊匹配成功的位置數*右邊匹配成功的位置數就行了

所以對preh[i]和sufh[i]做一次前綴和與後綴和

時間復雜度O(n+m+k*m)

1003(區間分解質因數)

題意:

為了計算一個東西,需要將[l,r]內所有東西分解質因數,l<=r<=1e12,r-l<=1e6

分析:

類似區間篩

將[1..1e6]內所有的素數p去刷[l,r]內p的倍數,算一算對應數字的p的指數是多少

註意細節:

1、不需要將分解質因數的結果存下來,那樣會開很大的數組導致很慢,我們發現為了求因數個數,是可以每次累計求的

2、[l,r]內的數,在被[1,1e6]內的數全部刷完之後,有些數可能不是1,因為它們本身是大素數,所以需要考慮到這些數

3、r-l<=1e6說明區間內數有1e6+1個……這個坑了很久……

1004(分數規劃)

題意:

有一個長度為n(<=60000)的數組,數字是<=n的數字,一個區間[l,r]的價值是[l,r]內不同數字個數除以區間長度,求所有區間中價值最小為多少

分析:

一看到最優比率問題就想到分數規劃

二分答案mid,看是否有解,即判斷是否存在num[l..r]/(r-l+1)<=mid

分母乘到右邊,即是否num[l..r]<=r*mid-l*mid+mid

我們從小到大枚舉r,去快速尋找價值最小的l

轉換一下式子:num[l..r]+l*mid<=r*mid+mid

對於一個確定的r,尋找價值最小的l也就相當於用線段樹維護num[l..r]+l*mid的最小值即可

那麽r->r+1,會修改哪些信息呢?

我們可以記錄下數字a[l]的前一個同顏色的位置pre

那麽就相當於給[pre+1..r]這段區間加1

時間復雜度O(logA * nlogn)

1005(取模意義下的最短路)

題意:

有四個點四條邊組成了一個環,每條邊是長度<=30000的整數,還有一個大整數k(k<=10^8),我在起點2,最終我也要到達終點2,問所有可行的路徑總長度中,長度大於等於k的最短值是多少

分析:

第一眼感覺不能最短路

但是這題其實很套路,我們去考察起點相連的兩條邊,取兩者當中的一個最小值w,那麽如果我們可以走出長度x,那麽我們就必定可以走出長度x+2*w

我們可以建4*2*w個點,d[i][j]表示從起點走到點i結束,走的長度在模2*w為j的情況下最短路

對於最後的d[i][j],如果其值>=k,那麽就把它和ans取min;如果<k,那麽我們就把它一直+2*w,直到>=k,再和ans取min

時間復雜度O(8*w*logw)

1006(手寫bitset優化圖的dfs)

題意:

有n(<=500)個點的稠密圖,m(m<=250000)次修改,每次修改將原圖中不超過10條邊進行取反(原本連上的就斷開,原本斷開的就連上),對於每次修改之後,都要求出圖的強連通分量

分析:

我們先來算一算暴力的復雜度:因為是稠密圖,我們用鄰接矩陣來存,那麽對圖2次dfs的復雜度就是n^2,總的復雜度就是62500*25000=1.5*10^9,無法接受

我們可以考慮用bitset來優化

我們可以開一個bitset表示當前未訪問過的點的集合,另一個bitset表示某個點u的相鄰點的集合,將兩個bitset and一下,那麽我們去跑那些為1的位置

這樣總的復雜度就除以了64,變成了3*10^7,就行了

但是用stl裏的bitset有個問題,它不支持取出所有為1的位,你還是只能一個一個去枚舉,這會導致復雜度並沒有降下來,解決方法是自己手寫一個bitset(%%%%csy)

1007(拓撲排序+dfs找環)

題意:

有一個左邊n個點,右邊n個點(n<=300000)的二分圖,左邊的每個點都向右邊的兩個點連出兩條有權值的邊,對於一個完美匹配,匹配的價值是所有匹配邊邊權的乘積,求所有完美匹配的價值和

分析:

很容易發現如果右邊某個點度數是1,那麽完美匹配的這條邊就固定了,同時它對應的左邊那個點度數也就變成1了,那麽它剩下的那個邊也是固定的了

所以我們可以先拓撲排序把這些固定的匹配給篩出來

那麽還剩下來左邊m個點,每個點度數都是2,右邊剩下m個點,度數都>1

又因為左邊度數一共2m,所以右邊必然也是每個點度數都是2

那麽就分成了連通塊,我們發現可以分別去處理這些連通塊,最後答案相乘

對於一個連通塊,它一定會形成一個環,而且是偶環(奇環不存在於二分圖),那就相當於這個連通塊的完美匹配有兩種方式,一種是取1 3 5 7 9邊,另一種是取2 4 6 8 10邊,只要去dfs找出這個環,算出兩種方式各自的乘積就行了

1008(MST+並查集)

題意:

n(<=100000)組成一個數,有m個修改(a,b,c,d,w)表示將樹上a->b的點、樹上c->d的點,以及它們彼此之間都連上權值為w的邊,最後求最小生成樹

分析:

類似Kruskal算法,我們按w排序,然後去用並查集維護是否出現環

很明顯對於一個詢問,我們只需要把a,b均走到它們的lca,將路中相鄰的點在並查集中連邊即可,cd也是一樣的道理,最後將兩者的lca在並查集中合並就行了

但是問題上我們不能對於每個詢問都去將路徑上所有點都走慢

我們發現有很多鏈之前都已經走過了,我們可以再開一個並查集,f[i]表示樹上連續鏈當中,i往上最高能走到f[i]

時間復雜度O(mlogm)

1009(構造)

水題略過,容易構造出模數為2的答案

1010(DP)

待填坑

1011(模擬)

1012(遞推)

題意:

有長度為n的a數組和長度為m的b數組,n,m<=2000,問a和b的所有共同子序列中有多少個是擺動數列

分析:

很容易寫出一個遞推,f[i][j]表示a的前i個,b的前j個,以a[i],b[j]為結尾的,最後的上升/下降趨勢為k的結果

f[i][j][k]=Σf[i‘][j‘][1-k] (a[i‘]==b[j‘],a[i‘]<a[i]或者a[i‘]>a[i])

這樣是O(n^4)的,並不可行

我們發現這個遞推式子其實是求[1..i-1][1..j-1][1..a[i]-1]的前綴和,這個維數挺高的……

但我們發現因為i是單增的,所以第一維可以直接省掉,a[j][num]表示當前i下,最後一個位置是b[1]..b[j-1],並且數字大小<=num的f值的前綴和

這樣可以用一個二維樹狀數組來維護

時間復雜度是O(n^2log^2n)的,恰好可以卡過去

不過有更優秀的方法

我們在從小到大枚舉j的過程中,我們可以根據b[j]和a[i]的大小關系來決定這個j作為前一個波峰/波谷的答案,用一個cnt0和cnt1來將所有b[j]!=a[i]位置的貢獻加起來就行了(貢獻就是對應的前綴和)

對於b[j]=a[i]的位置,就可以直接根據cnt0和cnt1來O(1)更新遞推的答案

所有j枚舉完了,要記得將這次的f值也要放入前綴和當中

這樣復雜度就是O(n^2)的了

1013

待填坑

2017多校Round4(hdu6067~hdu6079)