想定が平方分割なのは想定外だった…。
https://yukicoder.me/problems/no/1031
問題
1~NのPermutationである数列が与えられる。
このうち長さ2以上の区間を選び、その中の最大値と最小値を入れ替えるという動作を行う。
得られる数列は何通りか。
解法
入れ替え云々は置いておいて、区間を選んだ時の最大値と最小値の組み合わせを求めればよい。
最大値の大きな順に処理していくことを考える。
今見ている(最大値とする)要素を右端として、左端を左に伸ばしたとき、最小値が何回更新されるかというのと(ただし途中で自身より大きな値より左に伸ばすことができない)、同様に今見ている要素を左端として、右端を右に伸ばしたとき、最小値が何回更新されるかというのを求めればよい。
そのためには、自身に最寄りの自身より小さい値の位置を、左右それぞれ求めておこう。
さらにそれらをダブリングしておくと、二分探索の要領で左右それぞれ最小値が何回更新されるかO(logN)で求められるようになる。
int N; int P[101010]; int L[101010][21],R[101010][21]; void solve() { int i,j,k,l,r,x,y; string s; cin>>N; vector<pair<int,int>> cand; FOR(i,N) { cin>>P[i+1]; cand.push_back({P[i+1],i+1}); } FOR(j,20) R[N+1][j]=N+1; vector<int> V; FOR(i,N+1) { while(V.size()&&P[V.back()]>P[i]) V.pop_back(); if(V.size()) L[i][0]=V.back(); V.push_back(i); } V.clear(); V.push_back(N+1); for(i=N;i>=1;i--) { while(V.size()&&P[V.back()]>P[i]) V.pop_back(); if(V.size()) R[i][0]=V.back(); V.push_back(i); } FOR(i,20) FOR(j,N) L[j+1][i+1]=L[L[j+1][i]][i],R[j+1][i+1]=R[R[j+1][i]][i]; set<int> D; D.insert(0); D.insert(N+1); sort(ALL(cand)); reverse(ALL(cand)); ll ret=0; FORR(c,cand) { x=c.second; auto it=D.insert(x).first; i=*prev(it); j=*next(it); int cur=x; for(y=19;y>=0;y--) if(R[cur][y]<j) cur=R[cur][y],ret+=1<<y; cur=x; for(y=19;y>=0;y--) if(L[cur][y]>i) cur=L[cur][y],ret+=1<<y; } cout<<ret<<endl; }
まとめ
こういうのCSAやCodeforcesで多いイメージ。