// #define _GLIBCXX_DEBUG // for STL debug (optional) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using ll = long long int; using int64 = long long int; template void chmax(T &a, T b) {a = max(a, b);} template void chmin(T &a, T b) {a = min(a, b);} template void chadd(T &a, T b) {a = a + b;} int dx[] = {0, 0, 1, -1}; int dy[] = {1, -1, 0, 0}; const int INF = 1LL << 29; const ll LONGINF = 1LL << 60; const ll MOD = 1000000007LL; // @category セグメント木 (Segment Tree) // @title 遅延伝播セグメント木 (Lazy Segment Tree) template struct LazySegmentTree { using MMtoM = function< MonoidType(MonoidType, MonoidType) >; using OOtoO = function< OperatorType(OperatorType, OperatorType) >; using MOtoM = function< MonoidType(MonoidType, OperatorType) >; using OItoO = function< OperatorType(OperatorType, int) >; // node, lazy, update flag (for lazy), identity element int n; vector node; vector lazy; vector need_update; MonoidType E0; OperatorType E1; // update / combine / lazy / accumulate function MOtoM upd_f; MMtoM cmb_f; OOtoO lzy_f; OItoO acc_f; void build(int m, vector v = vector()) { if(v != vector()) m = v.size(); n = 1; while(n < m) n *= 2; node = vector(2*n-1, E0); lazy = vector(2*n-1, E1); need_update = vector(2*n-1, false); if(v != vector()) { for(int i=0; i=0; i--) { node[i] = cmb_f(node[2*i+1], node[2*i+2]); } } } // initialize LazySegmentTree() {} LazySegmentTree(int n_, MonoidType E0_, OperatorType E1_, MOtoM upd_f_, MMtoM cmb_f_, OOtoO lzy_f_, OItoO acc_f_, vector v = vector()) : E0(E0_), E1(E1_), upd_f(upd_f_), cmb_f(cmb_f_), lzy_f(lzy_f_), acc_f(acc_f_) { build(n_, v); } void eval(int k, int l, int r) { if(!need_update[k]) return; node[k] = upd_f(node[k], acc_f(lazy[k], r - l)); if(r - l > 1) { lazy[2*k+1] = lzy_f(lazy[2*k+1], lazy[k]); lazy[2*k+2] = lzy_f(lazy[2*k+2], lazy[k]); need_update[2*k+1] = need_update[2*k+2] = true; } lazy[k] = E1; need_update[k] = false; } void update(int a, int b, OperatorType x, int l, int r, int k) { eval(k, l, r); if(b <= l or r <= a) return; if(a <= l and r <= b) { lazy[k] = lzy_f(lazy[k], x); need_update[k] = true; eval(k, l, r); } else { int mid = (l + r) / 2; update(a, b, x, l, mid, 2*k+1); update(a, b, x, mid, r, 2*k+2); node[k] = cmb_f(node[2*k+1], node[2*k+2]); } } MonoidType query(int a, int b, int l, int r, int k) { if(b <= l or r <= a) return E0; eval(k, l, r); if(a <= l and r <= b) return node[k]; int mid = (l + r) / 2; MonoidType vl = query(a, b, l, mid, 2*k+1); MonoidType vr = query(a, b, mid, r, 2*k+2); return cmb_f(vl, vr); } // update [a, b)-th element (applied value, x) void update(int a, int b, OperatorType x) { update(a, b, x, 0, n, 0); } // range query for [a, b) MonoidType query(int a, int b) { return query(a, b, 0, n, 0); } void dump() { fprintf(stderr, "[lazy]\n"); for(int i=0; i<2*n-1; i++) { if(i == n-1) fprintf(stderr, "xxx "); if(lazy[i] == E1) fprintf(stderr, " E "); else fprintf(stderr, "%3d ", lazy[i]); } fprintf(stderr, "\n"); fprintf(stderr, "[node]\n"); for(int i=0; i<2*n-1; i++) { if(i == n-1) fprintf(stderr, "xxx "); if(node[i] == E0) fprintf(stderr, " E "); else fprintf(stderr, "%3d ", node[i]); } fprintf(stderr, "\n"); } }; // @category セグメント木 (Segment Tree) // @title セグメント木 (Segment Tree) // 抽象 SegmentTree (0-indexed・一点更新・区間取得) template struct SegmentTree { using Function = function< MonoidType(MonoidType, MonoidType) >; // node, identity element int n; vector node; MonoidType E0; // update / combine function Function upd_f, cmb_f; void build(int m, vector v = vector()) { if(v != vector()) m = v.size(); n = 1; while(n < m) n *= 2; node = vector(2*n-1, E0); if(v != vector()) { for(int i=0; i=0; i--) { node[i] = cmb_f(node[2*i+1], node[2*i+2]); } } } // initialize SegmentTree() {} SegmentTree(int n_, MonoidType E0_, Function upd_f_, Function cmb_f_, vector v = vector()) : E0(E0_), upd_f(upd_f_), cmb_f(cmb_f_) { build(n_, v); } // update k-th element (applied value: x) void update(int k, MonoidType x) { k += n - 1; node[k] = upd_f(node[k], x); while(k > 0) { k = (k - 1) / 2; node[k] = cmb_f(node[2*k+1], node[2*k+2]); } } // range query for [a, b) // 非再帰のアイデア: http://d.hatena.ne.jp/komiyam/20131202/1385992406 MonoidType query(int a, int b) { MonoidType vl = E0, vr = E0; for(int l=a+n, r=b+n; l>=1, r>>=1) { if(l & 1) vl = cmb_f(vl, node[(l++)-1]); if(r & 1) vr = cmb_f(node[(--r)-1], vr); } return cmb_f(vl, vr); } }; // Mo のアルゴリズム (以下の条件を満たすときにクエリを高速に処理可能) // ・オフラインクエリ // ・クエリの対象となる数列が不変 // ・区間の伸縮にかかる計算量が小さい struct Mo { int bucket, nl, nr, ptr; vector left, right, query_idx; vector state; Mo(int N) : bucket(sqrt(N) + 1), nl(0), nr(N), ptr(0), state(N, true) { left = right = vector(); } // 区間 [l, r) を追加 void insert(int l, int r) { left.push_back(l); right.push_back(r); } // ソートする (バケットごとに、バケット同じなら右端小さい順) void build() { query_idx = vector(left.size()); iota(query_idx.begin(), query_idx.end(), 0); sort(query_idx.begin(), query_idx.end(), [&](int a, int b) { if(left[a] / bucket != left[b] / bucket) return left[a] < left[b]; return right[a] < right[b]; }); } // クエリを一つすすめて、そのクエリ id を返す int proceed() { if(ptr == query_idx.size()) return -1; int id = query_idx[ptr]; while(nl > left[id] ) operate(--nl, true); while(nr < right[id]) operate(nr++, false); while(nl < left[id] ) operate(nl++, true); while(nr > right[id]) operate(--nr, false); return query_idx[ptr++]; } void operate(int idx, bool left) { state[idx].flip(); if(state[idx]) add(idx, left); else del(idx, left); } void add(int idx, bool left); void del(int idx, bool left); }; // res := その区間由来の転倒数 int N, Q; ll res = 0, pls = 0; vector A; SegmentTree sl, sm, sr; LazySegmentTree sa; void Mo::add(int idx, bool left) { if(left) { sl.update(A[idx], -1); sm.update(A[idx], +1); sa.update(0, A[idx], -1); res += sl.query(A[idx]+1, N); res += sr.query(0, A[idx]); } else { sr.update(A[idx], -1); sm.update(A[idx], +1); sa.update(A[idx]+1, N, -1); res += sl.query(A[idx]+1, N); res += sr.query(0, A[idx]); } pls = sa.query(0, N); } void Mo::del(int idx, bool left) { if(left) { sl.update(A[idx], +1); sm.update(A[idx], -1); sa.update(0, A[idx], +1); res -= sl.query(A[idx]+1, N); res -= sr.query(0, A[idx]); } else { sr.update(A[idx], +1); sm.update(A[idx], -1); sa.update(A[idx]+1, N, +1); res -= sl.query(A[idx]+1, N); res -= sr.query(0, A[idx]); } pls = sa.query(0, N); } int main() { scanf("%d%d", &N, &Q); A.resize(N); for(int i=0; i(N, 0, [](int a, int b) { return a + b; }, [](int a, int b) { return a + b; }); sm = sr = sl; // 区間 add, 区間 min sa = LazySegmentTree(N, INF, 0, [](int a, int b) { return a + b; }, [](int a, int b) { return min(a, b); }, [](int a, int b) { return a + b; }, [](int a, int x) { return a; }, vector(N, 0)); ll inv = 0; { SegmentTree seg_inv(N, 0, [](int a, int b) { return a + b; }, [](int a, int b) { return a + b; }); vector ord(N); iota(ord.begin(), ord.end(), 0); sort(ord.begin(), ord.end(), [&](int x, int y) { if(A[x] != A[y]) return A[x] > A[y]; return x > y; }); for(int i=0; i len(Q); for(int i=0; i ans(Q); for(int i=0; i