HDU - 5788 Level Up(主席树+dfs序+树状数组)
題目鏈接:點擊查看
題目大意:給出一棵有向樹,每個節(jié)點都有一個初始的權(quán)值 a[ i ] ,和一個通過計算得到的權(quán)值 mid[ i ] ,mid 數(shù)組的計算方法如下:mid[ u ] 為結(jié)點 u 及其子樹中所有?a[ i ] 的中位數(shù),現(xiàn)在問如果可以令其中一個節(jié)點的 a[ i ] 變?yōu)?1e5,如何選擇可以使得所有節(jié)點的 mid[ i ] 之和最大
題目分析:乍一看題目可能比較復雜,但是一步一步分析下來可能就能找到突破口了
首先是子樹,對于子樹上進行操作,我們可以跑出整棵樹的dfs序,這樣就將子樹上的操作轉(zhuǎn)換為區(qū)間上的操作了
其次是中位數(shù),經(jīng)過上面dfs序的轉(zhuǎn)換,我們現(xiàn)在的目標轉(zhuǎn)換為了如何求一段區(qū)間上的中位數(shù),換句話說,設 sz[ i ] 為點 i 的子樹大小,現(xiàn)在需要求一段區(qū)間上第 ( sz[ i ] + 1 ) / 2 大的數(shù)是什么,區(qū)間上第 k 大的數(shù),這里可以用主席樹解決
再分析一下題意,將一個節(jié)點的權(quán)值 a 變?yōu)?1e5 ,1e5 在這個題目中相當于無窮大,如果節(jié)點 i 的 a[ i ] 變?yōu)?1e5 后,根據(jù)題意可知,其只會對特定的祖先產(chǎn)生影響,這些特定的祖先 x 必須滿足 mid[ x ] >= a[ i ] ,這樣當 a[ i ] 變?yōu)?1e5 后,祖先 x 的中位數(shù)就會變大為原本子樹中第 ( sz[ i ] + 1 ) / 2 + 1 大的數(shù)了
這樣我們可以通過dfs序和主席樹,將 cur_mid[ i ] 和 next_mid[ i ] 預處理出來,其意義分別為結(jié)點 i 的中位數(shù)和結(jié)點 i 的中位數(shù)的下一個數(shù)
接下來就可以暴力枚舉每個頂點,然后計算當前節(jié)點的權(quán)值 a 變?yōu)?1e5 后的影響了,不難發(fā)現(xiàn),如果某一個權(quán)值 a[ i ] 變?yōu)?1e5 后,總的答案只會變大,不會變小,那么我們不妨設 sum 為所有 cur_mid[ i ] 之和,mmax 為變化后可以增加的答案,那么最終答案就是 sum?+ mmax 了
關(guān)于如何暴力枚舉,我們可以借助 dfs 和樹狀數(shù)組來實現(xiàn),樹狀數(shù)組中的每個點,其下標的意義是:祖先的中位數(shù)大小,維護的權(quán)值的意義是:如果更改子樹中的結(jié)點 a[ v?] 為 1e5 后,當前節(jié)點可以提供的貢獻
這樣在枚舉點 i 時,將其變化的值插入到樹狀數(shù)組中,也就是 next_mid[ i ] - cur_mid[ i ] 插入到位置?cur_mid[ i ] 中,同時查詢點 i 的權(quán)值 a 更改為 1e5 后祖先節(jié)點的貢獻,也就是 query( 100000 ) - query( a[ i ] - 1 ) 了,其意義就是查詢祖先的中位數(shù)位于 [ a[ i ] , inf?] 中的貢獻
代碼:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;vector<int>node[N];//鄰接表 int tot,L[N],R[N],sz[N];//樹鏈剖分用 int cur_mid[N],next_mid[N];//題目用 LL sum,mmax; /*樹狀數(shù)組*/ LL c[N];int lowbit(int x) {return x&(-x); }void update(int pos,int val) {while(pos<N){c[pos]+=val;pos+=lowbit(pos);} }LL query(int pos) {LL ans=0;while(pos){ans+=c[pos];pos-=lowbit(pos);}return ans; } /*樹狀數(shù)組*/ /*主席樹*/ struct Node {int l,r;int sum; }tree[N*20];int cnt,root[N],a[N];void update(int num,int &k,int l,int r) {tree[cnt++]=tree[k];k=cnt-1;tree[k].sum++;if(l==r)return;int mid=l+r>>1;if(num<=mid)update(num,tree[k].l,l,mid);elseupdate(num,tree[k].r,mid+1,r); }int query(int i,int j,int k,int l,int r)//i:左端點根節(jié)點編號,j:右端點根節(jié)點編號,k:第k大的數(shù),l,r:區(qū)間[l,r] {int d=tree[tree[j].l].sum-tree[tree[i].l].sum;if(l==r)return l;int mid=l+r>>1;if(k<=d)return query(tree[i].l,tree[j].l,k,l,mid);elsereturn query(tree[i].r,tree[j].r,k-d,mid+1,r); } /*主席樹*/ void dfs(int u) {sz[u]=1;L[u]=++tot;root[tot]=root[tot-1];update(a[u],root[tot],1,N);for(auto v:node[u]){dfs(v);sz[u]+=sz[v];}R[u]=tot;if(sz[u]==1){cur_mid[u]=a[u];next_mid[u]=100000;sum+=cur_mid[u];}else{int mid=(sz[u]+1)>>1;cur_mid[u]=query(root[L[u]-1],root[R[u]],mid,1,N);next_mid[u]=query(root[L[u]-1],root[R[u]],mid+1,1,N);sum+=cur_mid[u];} }void DFS(int u) {update(cur_mid[u],next_mid[u]-cur_mid[u]);mmax=max(mmax,query(100000)-query(a[u]-1));for(auto v:node[u])DFS(v);update(cur_mid[u],cur_mid[u]-next_mid[u]); }void init() {for(int i=0;i<N;i++)node[i].clear();root[0]=0;tree[0].l=tree[0].r=tree[0].sum=0;cnt=1;sum=tot=mmax=0; }int main() { #ifndef ONLINE_JUDGE // freopen("input.txt","r",stdin); // freopen("output.txt","w",stdout); #endif // ios::sync_with_stdio(false);int n;while(scanf("%d",&n)!=EOF){init();for(int i=1;i<=n;i++)scanf("%d",a+i);for(int i=2;i<=n;i++){int x;scanf("%d",&x);node[x].push_back(i);}dfs(1);DFS(1);printf("%lld\n",mmax+sum);}return 0; }?
總結(jié)
以上是生活随笔為你收集整理的HDU - 5788 Level Up(主席树+dfs序+树状数组)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CodeForces - 1325F E
- 下一篇: CodeForces - 1368D A