BZOJ4919 大根堆 線段樹合併 二分 離散化

2021-09-10 05:53:01 字數 3545 閱讀 6553

題目鏈結

題意:給你一棵樹,每個點有點權,問你最多能選出多少個點,使得所有選出的點中子節點的權值都比父節點小(嚴格小於)。點數2e5,權值1e9

題解:首先的乙個暴力是用乙個樹形dp,dp[

x][i

]dp[x][i]

dp[x][

i]表示點x

xx為根的子樹內,最大權值是i

ii時子樹內最多選的點數。我們不難發現,隨著這個i

ii的增大,最多選出的點數也是單調不降的。於是我們考慮從子節點轉移過來,dp[

x][i

]=ma

x(∑y

∈son

[x]d

p[y]

[i],

∑y∈s

on[x

]dp[

x][i

−1]+

1)

dp[x][i]=max(\sum_dp[y][i],\sum_dp[x][i-1]+1)

dp[x][

i]=m

ax(∑

y∈so

n[x]

​dp[

y][i

],∑y

∈son

[x]​

dp[x

][i−

1]+1

)。前一種是不選當前點,後一種是選了當前點,應該不難理解。我們發現其實第二維只與點的大小關係有關,與具體的點權值沒有關係,於是我們離散化一下,就可以得到乙個o(n

2)

o(n^2)

o(n2

)的演算法。

我們考慮能不能對這個dp進行優化。我們發現,如果對於當前x

xx,你有了每乙個i

ii的∑y∈

son[

x]dp

[y][

i]

\sum_dp[y][i]

∑y∈son

[x]​

dp[y

][i]

,那麼我們考慮當前點更新答案時可能更新哪些點。我們不難發現,由於∑y∈

son[

x]dp

[y][

i]

\sum_dp[y][i]

∑y∈son

[x]​

dp[y

][i]

是單調遞增的,所以能更新的區間也一定是一段連續的區間。而這個連續的區間的乙個端點一定是當前x

xx點的值,顯然是不可能更新更小的權值時的答案的。於是我們可以二分一下右端點。我們發現,我們找到這個區間後就只需要維護乙個區間加的操作,這個可以線段樹來做。而對於整個樹形結構,我們可以用乙個線段樹合併來從兒子到父親更新答案,線段樹的權值i

ii維護的就是dp[

x][i

]dp[x][i]

dp[x][

i]的答案。這樣就可以做到o(n

log2

n)

o(nlog^2n)

o(nlog

2n),是可以通過本題的。

然而由於空間複雜度是o(n

logn

)o(nlogn)

o(nlog

n)的,於是我因為陣列開得過大而tle掉了。。。

**:

#include

#include

#include

#include

#include

#include

using

namespace std;

int n,fa[

200010

],hed[

200010

],cnt,num,val[

200010

],ans;

int b[

200010

],root[

200010

],qwq;

struct node

a[400010];

struct tree

tr[4000100];

inline

intread()

return x;

}inline

void

add(

int from,

int to)

inline

void

merge

(int

&l,int r)

tr[l]

.mx=tr[l]

.mx+tr[r]

.mx;

merge

(tr[l]

.l,tr[r]

.l);

merge

(tr[l]

.r,tr[r]

.r);

}inline

intquery

(int rt,

int l,

int r,

int x)

inline

void

update

(int

&rt,

int l,

int r,

int le,

int ri)

int mid=

(l+r)

>>1;

if(le<=mid)

update

(tr[rt]

.l,l,mid,le,ri);if

(mid+

1<=ri)

update

(tr[rt]

.r,mid+

1,r,le,ri);}

inline

void

dfs(

int x)

int ji=

query

(root[x],1

,qwq,val[x]-1

)+1;

if(ji<=

query

(root[x],1

,qwq,val[x]))

return

;int l=val[x]

,r=qwq,mid,res=val[x]

;while

(l<=r)

else

r=mid-1;

}update

(root[x],1

,qwq,val[x]

,res);}

intmain()

sort

(b+1

,b+n+1)

; qwq=

unique

(b+1

,b+n+1)

-b-1

;for

(int i=

1;i<=n;

++i)

val[i]

=lower_bound

(b+1

,b+qwq+

1,val[i]

)-b;

dfs(1)

; ans=

query

(root[1]

,1,qwq,qwq)

;printf

("%d\n"

,ans)

;return0;

}

題解 BZOJ4919 大根堆

題面 傳送門。老師說今天要考一道線段樹合併,然後。然後這道題我就gg了。當然可以用線段樹合併寫,只是比較複雜 有人賽時想了個貪心,然後被機房巨佬hack了,結果在hack的過程中巨佬想出了正解。貪心思路 對於乙個節點,取右邊的 大一點的 肯定更優。其實很好hack啊,隨便搞一條鏈就可以了 ac思路 ...

BZOJ4919 大根堆 樹上LIS

題目描述見鏈結 樹上 lis lisli s 問題,使用std multisetst維護當前子樹內所有可能的 lis lisli s 結尾,從前往後 lis lisli s結尾 對應的長度遞增 子樹之間互不影響,只需考慮子樹根節點 u uu 對子樹內的影響,模擬 序列lis lisli s 的做法,...

bzoj 4919 大根堆(set啟發式合併)

傳送門biu 假設是在序列上,就變成了nlogn的dp求最長上公升子串行問題 假設是在樹上,我們只需要在每個節點存下dp陣列,然後用set的啟發式合併將dp陣列合併就可以了 代替splay include define n 200005 using namespace std vector e n ...