日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

【线段树】【FeyatCup】——2.法法塔的奖励

發布時間:2025/7/25 189 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【线段树】【FeyatCup】——2.法法塔的奖励 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先感謝并膜拜兩位大佬 wyl8899 & 法法塔 感謝兩位的出題!

題目:

法法塔是很喜歡寫程序的。所以冒著就算碼農屌絲一輩子的風險也大無畏地寫著程序。
碼農們為了表彰法法塔的堅持與執著,決定給法法塔頒布獎勵,為了體現碼農的屌絲氣質,獎勵也將由法法塔自己做出選擇!
所有的獎勵被組織成了一棵樹的形態,每個點都有一個權值。法法塔首先選擇一個子樹,然后選擇從該子樹內的一個葉子節點到該子樹的根的一條路徑,將路徑上節點的權值依次排成一個序列,求得這個序列的最長不下降子序列長度,這個長度就是他能得到的獎品數目。要求該子樹的根必須被選擇。
接下來就是法法塔進行選擇的時間了。盡可能多地得到獎品是自然的。那么法法塔到底能夠得到多少獎品呢?這是個很糾結的問題,他決定交給你來解決。對于每個子樹,他都希望你能求出他首先選擇了這個子樹后,他能得到的最多的獎品數目。

輸入數據:

第一行一個數n,描述樹上節點的個數。
接下來一行n個數,描述樹上每個點的父親,點1為根,其父親設為-1,其余每個點的父親的標號都小于自己。?
接下來一行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.法法塔的奖励的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。