Official

G - Another Shuffle Window Editorial by en_translator


When \(P_L,P_{L+1},\dots,P_R\) is shuffled, how the inversion number will change?

  • Block \(X\)\(P_1,P_2,\dots,P_{L-1}\)
  • Block \(Y\)\(P_L,P_{L+1},\dots,P_R\)
  • Block \(Z\)\(P_{R+1},P_{R+2},\dots,P_N\)

Consider the relative position of the elements \(P_i\) and \(P_j\) \(i<j\) after the shuffle.

  • If \(P_i\) belongs to block \(X\) and \(P_j\) to block \(X\)\(P_i\) still comes earlier than \(P_j\) after the shuffle.
  • If \(P_i\) belongs to block \(X\) and \(P_j\) to block \(Y\)\(P_i\) still comes earlier than \(P_j\) after the shuffle.
  • If \(P_i\) belongs to block \(X\) and \(P_j\) to block \(Z\)\(P_i\) still comes earlier than \(P_j\) after the shuffle.
  • If \(P_i\) belongs to block \(Y\) and \(P_j\) to block \(Y\) … After the shuffle, \(P_i\) comes earlier than \(P_j\) with the probability \(1/2\), and \(P_j\) comes earlier than \(P_i\) with the probability \(1/2\).
  • If \(P_i\) belongs to block \(Y\) and \(P_j\) to block \(Z\)\(P_i\) still comes earlier than \(P_j\) after the shuffle.
  • If \(P_i\) belongs to block \(Z\) and \(P_j\) to block \(Z\)\(P_i\) still comes earlier than \(P_j\) after the shuffle.

Thus, the relative position of two elements are preserved unless they are both in block Y, while block Y is shuffled uniformly at random.
By this discussion, it turns out that the expected inversion number is (the inversion number of original \(P\)) \(-\) (the inversion number of original \(P_L,P_{L+1},\dots,P_R\)\(+\) \(((_{R-L+1}C_2)/2)\)).

All that left is to find the inversion number of \(P_i,P_{i+1},\dots,P_{i+K-1}\) for all \(1 \le i \le N-K+1\).

This can be found as follows:

  • First, find the inversion number of \(P_1,P_2,\dots,P_K\), denoting it by \(I\).
  • Then, for \(i=K+1,K+2,\dots,N\), repeat the following:
    • Subtract from \(I\) the number of elements less than \(P_{i-K}\) among \(P_{i-K+1},P_{i-K+2},\dots,P_{i-1}\).
    • Then, add to \(I\) the number of elements greater than \(P_{i-K+1},P_{i-K+2},\dots,P_{i-1}\) among \(P_{i-K+1},P_{i-K+2},\dots,P_{i-1}\).
    • At this point, \(I\) is the inversion number of \(P_{i-K+1},P_{i-K+2},\dots,P_i\).

This can be evaluated by processing segment-sum queries (using a data structure like a segment tree).

Sample code (C++):

#include<bits/stdc++.h>
#include<atcoder/segtree>

using namespace std;
using namespace atcoder;

#define mod 998244353
#define inv2 499122177

long long op(long long a,long long b){
  if((a+b)>=mod){
    return (a+b)-mod;
  }
  return (a+b);
}
long long e(){
  return 0;
}

long long power(long long a,long long b){
  long long x=1,y=a;
  while(b>0){
    if(b&1ll){
      x=(x*y)%mod;
    }
    y=(y*y)%mod;
    b>>=1;
  }
  return x%mod;
}

long long modular_inverse(long long n){
  return power(n,mod-2);
}

int main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  long long n,k;
  cin >> n >> k;
  vector<long long> p(n);
  for(auto &nx : p){
    cin >> nx;
    nx--;
  }

  long long bas=0;
  {
    segtree<long long,op,e> seg(n);
    for(long long i=0;i<n;i++){
      bas+=seg.prod(p[i],n);
      seg.set(p[i],1);
    }
  }

  long long res=0;
  long long sub=0;
  long long add=((k*(k-1))/2)%mod; add*=inv2; add%=mod;

  segtree<long long,op,e> seg(n);
  for(long long i=0;i<k;i++){
    sub+=seg.prod(p[i],n); sub%=mod;
    seg.set(p[i],1);
  }

  for(long long i=k;i<=n;i++){
    long long cur=(mod+bas-sub+add)%mod;
    res+=cur; res%=mod;
    if(i==n){break;}

    seg.set(p[i-k],0);
    sub+=(mod-seg.prod(0,p[i-k])); sub%=mod;
    sub+=seg.prod(p[i],n); sub%=mod;
    seg.set(p[i],1);
  }

  res*=modular_inverse(n-k+1); res%=mod;
  cout << res << "\n";
  return 0;
}

posted:
last update: