[bzoj4919]大根堆
阿新 • • 發佈:2018-08-14
long long i+1 註意 可選 strong 線段樹 ise tro pre
https://www.zybuluo.com/ysner/note/1248857
題面
給定一棵\(n\)個節點的有根樹,編號依次為\(1\)到\(n\),其中\(1\)號點為根節點。每個點有一個權值\(v_i\)。
選擇盡可能多的節點,使對於任意兩個點\(i,j\),如果\(i\)在樹上是\(j\)的祖先,那麽\(v_i>v_j\)。
請計算可選的最多的點數,註意這些點不必形成這棵樹的一個連通子樹。
\(n\leq2*10^5\)
解析
這題無非想讓選出的這些點自下往上都能構成最長上升子序列。
於是自下往上每條鏈都維護一下最長上升子序列。
如果兩鏈相交,把兩條序列直接合並(因不影響答案)。
然後嘗試把交點(\(LCA\)
關鍵是怎麽維護這東西。
線段樹合並???怒碼\(3KB\)???
\(set\)神器拯救一切。
而且由於合並時兩鏈(樹)互不影響,可以啟發式合並。(如果父親已有序列長度小於兒子,可以父子完全交換)。
復雜度\(O(nlog^2n)\)。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<set> #define re register #define il inline #define ll long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define fp(i,a,b) for(re int i=a;i<=b;i++) #define fq(i,a,b) for(re int i=a;i>=b;i--) using namespace std; const int mod=1e9+7,N=2e5+100,M=3000; struct Edge{int to,nxt;}e[N<<1]; int n,h[N],cnt,w[N]; multiset<int>f[N]; il void add(re int u,re int v) { e[++cnt]=(Edge){v,h[u]};h[u]=cnt; e[++cnt]=(Edge){u,h[v]};h[v]=cnt; } il ll gi() { re ll x=0,t=1; re char ch=getchar(); while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar(); if(ch==‘-‘) t=-1,ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-48,ch=getchar(); return x*t; } il void dfs(re int u,re int fa) { for(re int i=h[u];i+1;i=e[i].nxt) { re int v=e[i].to; if(v==fa) continue; dfs(v,u); if(f[v].size()>f[u].size()) swap(f[v],f[u]); for(set<int>::iterator j=f[v].begin();j!=f[v].end();j++) f[u].insert(*j); f[v].clear(); } if(f[u].size()>0&&f[u].lower_bound(w[u])!=f[u].end()) f[u].erase(f[u].lower_bound(w[u])); f[u].insert(w[u]); } int main() { memset(h,-1,sizeof(h)); n=gi(); fp(i,1,n) w[i]=gi(),add(i,gi()); dfs(1,0); printf("%lld\n",1ll*f[1].size()); return 0; }
[bzoj4919]大根堆