【线段树】【FeyatCup】——2.法法塔的奖励
首先感謝并膜拜兩位大佬 wyl8899 & 法法塔 感謝兩位的出題!
題目:
法法塔是很喜歡寫程序的。所以冒著就算碼農屌絲一輩子的風險也大無畏地寫著程序。 輸入數據: 第一行一個數n,描述樹上節點的個數。 輸出數據: 一行n個數,第i個數表示法法塔選擇了以第i個節點為根的子樹后,他可以獲得的最多的獎品個數。 數據范圍: n<=100000,每個數的權值都是1到n的正整數。 |
拆分題意,這道題是求樹上求最長不下降子序列,但是如果將每一條邊單獨拿出來進行dp是會爆空間的,所以我們考慮別的方法。
?
?
?
?
參考:
?
https://www.cnblogs.com/chhokmah/p/10550155.html
?
https://www.cnblogs.com/Mychael/p/8665589.html
https://www.luogu.org/blog/wcr20020327/xin-ren-di-di-yi-ci-blog-xian-duan-shu
?
動態開點線段樹&&線段樹合并:
顧名思義,動態開點線段樹就是在線段樹的基礎上實現動態開點,達到減小空間的方法。與原來的線段樹有一點不同,動態開點線段樹擁有左兒子(ls)與右兒子(rs),便于進行線段樹與點之間的組合和更新。
舉個例子,單點構造是在原有線段樹上開啟新的點(要注意取地址符的使用):
?
1 void change(int &o,int l,int r,int a,int b){//a=pos,b=size 2 if (!o) o=++cnt; 3 if (l==r){ 4 g[o].sum=max(g[o].sum,b); 5 return; 6 } 7 int mid=(l+r)/2; 8 if (a<=mid) change(g[o].ls,l,mid,a,b); 9 else change(g[o].rs,mid+1,r,a,b); 10 g[o].sum=max(g[g[o].ls].sum,g[g[o].rs].sum); 11 }?
其余的不再贅述,而線段樹合并則是將兩個線段樹合并在一起(廢話),參見代碼:
?
1 //Mychael大大的代碼 2 int merge(int u,int v){ 3 if (!u) return v;//如果沒有u點,則返回v做根 4 if (!v) return u;//類似上面 5 int t = ++cnt;//如果兩邊都有,就新建t點作為根 6 sum[t] = sum[u] + sum[v];//融合兩個點,但是在這個程序中需要改為更新最大值 7 ls[t] = merge(ls[u],ls[v]);//遞歸修改 8 rs[t] = merge(rs[u],rs[v]); 9 return t; 10 }?
經過上面兩個操作,這道題的解法應該已經大致明晰了呢:
我們在一個子樹中找到當前最長不下降子序列中的最大值,并在其基礎上加1即可。
?
//continue #include<bits/stdc++.h> using namespace std; const int N=1e6+10; int head[N],cnt,num[N],rt[N],n,ans[N]; struct edge{int to,next; }e[N]; struct line{int ls,rs,sum; }tr[N<<1]; void addedge(int from,int to){e[++cnt].to=to;e[cnt].next=head[from];head[from]=cnt; } void change(int &o,int l,int r,int siz,int pos){ //動態開點 if(!o) o=++cnt;if(l==r){tr[o].sum=max(tr[o].sum,siz);return;}int mid=(l+r)>>1;if(pos<=mid) change(tr[o].ls,l,mid,siz,pos);else change(tr[o].rs,mid+1,r,siz,pos);tr[o].sum=max(tr[tr[o].ls].sum,tr[tr[o].rs].sum); } int query(int o,int l,int r,int a,int b){//查詢區間最大值if(!o) return 0;if(l==a&&r==b) return tr[o].sum;int mid=(l+r)>>1;if(b<=mid) return query(tr[o].ls,l,mid,a,b);else if(a>mid) return query(tr[o].rs,mid+1,r,a,b);else return max(query(tr[o].ls,l,mid,a,mid),query(tr[o].rs,mid+1,r,mid+1,b)); } int merge(int a,int b,int l,int r){if(!a||!b) return a+b;if(l==r){tr[a].sum=max(tr[a].sum,tr[b].sum);return a;}int mid=(l+r)>>1;tr[a].ls=merge(tr[a].ls,tr[b].ls,l,mid);tr[a].rs=merge(tr[a].rs,tr[b].rs,mid+1,r);tr[a].sum=max(tr[a].sum,tr[b].sum);return a; } void dfs(int o){//遍歷整個樹,將線段樹合并求最大值int j=0;for(int u=head[o];u;u=e[u].next){int v=e[u].to;dfs(v);j=merge(j,rt[v],1,n);}ans[o]=query(j,1,n,1,num[o])+1;change(j,1,n,ans[o],num[o]);rt[o]=j; } int main(){scanf("%d",&n);int fa;for(int i=1;i<=n;i++){scanf("%d",&fa);if(fa!=-1)addedge(fa,i);}for(int i=1;i<=n;i++) scanf("%d",&num[i]);cnt=0;dfs(1);for(int i=1;i<=n;i++) printf("%d ",ans[i]);return 0; }?
轉載于:https://www.cnblogs.com/Nelson992770019/p/11160434.html
總結
以上是生活随笔為你收集整理的【线段树】【FeyatCup】——2.法法塔的奖励的全部內容,希望文章能夠幫你解決所遇到的問題。

- 上一篇: Nginx 之五: Nginx服务器的负
- 下一篇: 微信小程序开发--数据绑定