最大のフェンウィックツリー

多くの人がフェンウィックの木について知っています。 多くの人が使用します。 ただし、フェンウィックツリーは最大/最小で見つけることができないと考えられています。

同様に、この操作には逆関数はありません。 ただし、アルゴリズムを少し変更すると、この問題も解決できます。

注意:この記事は、フェンウィックツリーが何であるかを知っている人のために書かれており、その修正について最大限説明しています。フェンウィックツリーが何であるかわからない人は、どこか、コーメン、 ハブに関する記事でも読むことをお勧めします



1.問題の声明


配列があります。 間隔の最大値を見つけるため、およびセルの1つの値を増やすために、多くの要求が行われます。

はい、増加しています。 セルの値を減らすことはできません。そうしないと、アルゴリズムが機能しません。



2.実際にアルゴリズム


クラス(FenwickTree)を作成しましょう。FenwickTreeには、Update(int i、double cost)とMax(int left、int right)の2つのメソッドがあり、それぞれi番目のセルの値を更新し、間隔の最大値を検索します。

フェンウィックツリーと同様に、pow(2、k)> = a.size()のようなk最小数が必要です。

ツリーを配列に保存します。



通常のフェンウィックの木との主な違い


通常のフェンウィックツリーのように、1つではなく2つの配列が必要です。それらを左右に呼び出しましょう。

左の配列のi番目のセルに、セグメント[iG(i)+ 1、i]の最大値を格納し、右の配列のi番目のセルに、i <powのセグメント[i、i + G(i)-1]の最大値を格納します(2、k)およびi = pow(2、k)のセグメント[i、i]上。



実際にクラス:



class FenwickTree { private: vector<double> a; vector<double> left; vector<double> right; public: void PreProc(); double Max(int left,int right); void Update(int i,double cost); };
      
      







PreProc()関数は、初期データをツリーに追加するために必要であり、驚くほど複雑に見えます:

 void FenwickTree::PreProc(){ for(int i=0;i<a.size();i++) Update(i+1,a[i]); }
      
      







私はあなたの注意を引きます、それはi + 1です 配列G(x)= x-(x&(x-1))の遷移関数はx> 0で機能します

それでは、更新関数を書きましょう。



 void FenwickTree::Update(int r,double cost) { a[r-1]=cost; int i=r; while(i<=pow(2.0,double(k))) { left[i]=max(left[i],cost); i=i+G(i); } i=r; while(i>0) { right[i]=max(right[i],cost); i=iG(i); } }
      
      







およびMax:



 double FenwickTree::Max(int l,int r){ double res=0; int i=l; while(i+G(i)<=r) { res=max(res,right[i]); i=i+G(i); } if(a[i-1]>res) ans=i; res=max(res,a[i-1]); i=r; while(iG(i)>=l) { res=max(res,left[i]); i=iG(i); } return res; }
      
      







以上です。



ご覧のとおり、最大のフェンウィックツリーは非常に簡単かつ迅速に記述できます(これは非常に重要です)。



All Articles