ykmakuのブログ

競技プログラミングをがんばるブログ

Atcoder Beginner Contest ABC 140 E - Second Sum

  • 問題

atcoder.jp

  • 解法

これと同じ感じでやる。
atcoder.jp
ある数Xが2番目に大きい区間Iはどういう特徴があるか考える。
よく考えるとIXより大きい数をちょうど1つ含んでいる区間である事がわかる。
Xより大きく、かつXより右側にあるもののうち一番左側にあるものをX_R、その一つ右側の数をX_{RR}とする。
同様にXより大きく、かつXより左側にあるもののうち一番右側にあるものをX_L、その一つ左側の数をX_{LL}とする。

Xが2番目に大きい区間X,X_Lを両方含みX_{LL},X_Rを含まない区間、またはX,X_Rを両方含みX_{L},X_{RR}を含まない区間のどちらかとなる。

X,X_Lを両方含みX_{LL},X_Rを含まない区間区間の左端が(X_{LL},X_L]、右端が[X,X_R)であるような区間であることがわかる。
f:id:ykmaku:20190909205502p:plain
このような区間の数はX_{LL},X_L,X,X_Rの数列中での位置をそれぞれpos_{LL},pos_L,pos,pos_Rとすれば(pos_L-pos_LL)\times(pos_R-pos)個ある。
X,X_Rを両方含みX_{L},X_{RR}を含まない区間の数も同様に求められる。

あとはXに対してX_{LL},X_L,X_R,X_{RR}の位置を求められれば良い。
これはXN\to1の順で見ていき、X_{LL},X_L,X_R,X_{RR}をの位置を求めたのち(これらはlower_boundで求めることができる)、multisetにXの位置posを挿入すれば良い。

  • 感想

multisetのイテレータを前から見るとmultiset内の要素を昇順に見れることを忘れていました...

#include <iostream>
#include <vector>
#include <set>


using namespace std;

typedef long long int ll;

#define rep(i,n) for(int i=0;i<(n);++i)


int main()
{
	int n;
	cin>>n;
	vector<ll> p(n),pos(n+1,0);
	rep(i,n){
		cin>>p[i];
		pos[p[i]] = i+1;
	}
	ll ans = 0;

	multiset<ll> s;
	s.insert(0);
	s.insert(0);
	s.insert(n+1);
	s.insert(n+1);
	for (ll now = n; now >= 1; now--){
		auto it = s.lower_bound(pos[now]);
		ll pos_r = *it;

		it++;
		ll pos_rr = *it;

		it--;
		it--;
		ll pos_l = *it;

		it--;
		ll pos_ll = *it;

		ans += now*(pos_r-pos[now])*(pos_l-pos_ll);
		ans += now*(pos_rr-pos_r)*(pos[now]-pos_l);

		s.insert(pos[now]);
	}
	cout<<ans<<endl;
	
	return 0;
}