題目鏈結
題意:給你一棵樹,每個點有點權,問你最多能選出多少個點,使得所有選出的點中子節點的權值都比父節點小(嚴格小於)。點數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 ...