日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

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

題目:

法法塔是很喜歡寫程序的。所以冒著就算碼農(nóng)屌絲一輩子的風(fēng)險也大無畏地寫著程序。
碼農(nóng)們?yōu)榱吮碚梅ǚㄋ膱猿峙c執(zhí)著,決定給法法塔頒布獎勵,為了體現(xiàn)碼農(nóng)的屌絲氣質(zhì),獎勵也將由法法塔自己做出選擇!
所有的獎勵被組織成了一棵樹的形態(tài),每個點都有一個權(quán)值。法法塔首先選擇一個子樹,然后選擇從該子樹內(nèi)的一個葉子節(jié)點到該子樹的根的一條路徑,將路徑上節(jié)點的權(quán)值依次排成一個序列,求得這個序列的最長不下降子序列長度,這個長度就是他能得到的獎品數(shù)目。要求該子樹的根必須被選擇。
接下來就是法法塔進(jìn)行選擇的時間了。盡可能多地得到獎品是自然的。那么法法塔到底能夠得到多少獎品呢?這是個很糾結(jié)的問題,他決定交給你來解決。對于每個子樹,他都希望你能求出他首先選擇了這個子樹后,他能得到的最多的獎品數(shù)目。

輸入數(shù)據(jù):

第一行一個數(shù)n,描述樹上節(jié)點的個數(shù)。
接下來一行n個數(shù),描述樹上每個點的父親,點1為根,其父親設(shè)為-1,其余每個點的父親的標(biāo)號都小于自己。?
接下來一行n個數(shù),表示樹上每個點的權(quán)值。

輸出數(shù)據(jù):

一行n個數(shù),第i個數(shù)表示法法塔選擇了以第i個節(jié)點為根的子樹后,他可以獲得的最多的獎品個數(shù)。

數(shù)據(jù)范圍:

n<=100000,每個數(shù)的權(quán)值都是1到n的正整數(shù)。

  拆分題意,這道題是求樹上求最長不下降子序列,但是如果將每一條邊單獨拿出來進(jì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

?

動態(tài)開點線段樹&&線段樹合并:

  顧名思義,動態(tài)開點線段樹就是在線段樹的基礎(chǔ)上實現(xiàn)動態(tài)開點,達(dá)到減小空間的方法。與原來的線段樹有一點不同,動態(tài)開點線段樹擁有左兒子(ls)與右兒子(rs),便于進(jìn)行線段樹與點之間的組合和更新。

  舉個例子,單點構(gòu)造是在原有線段樹上開啟新的點(要注意取地址符的使用):

?

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 }

?

經(jīng)過上面兩個操作,這道題的解法應(yīng)該已經(jīng)大致明晰了呢:

我們在一個子樹中找到當(dāng)前最長不下降子序列中的最大值,并在其基礎(chǔ)上加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){ //動態(tài)開點 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){//查詢區(qū)間最大值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; }

?

轉(zhuǎn)載于:https://www.cnblogs.com/Nelson992770019/p/11160434.html

總結(jié)

以上是生活随笔為你收集整理的【线段树】【FeyatCup】——2.法法塔的奖励的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。