題解 P5047 【[Ynoi2019模擬賽]Yuno loves sqrt technology II】

一種很是膩害的高科技。二次離線莫隊,\(stOlxlOrz\)node

首先咱們康康這道題的暴力莫隊怎麼作。c++

其實就是搞個權值樹狀數組,每次右指針右移的時候查詢一下當前區間 \([l,r]\) 有多少個數比它大便可。數組

其餘相似。優化

複雜度\(O(n\sqrt m\log n)\)spa

代碼:指針

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
    char cc = getchar(); int cn = 0, flus = 1;
    while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
    while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
    return cn * flus;
}
const int N = 100000 + 5 ;
#define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
#define lowbit(x) ( x & ( - x ) )
int n, m, tot, ans[N] ;
int tree[N], c[N] ; 
struct Q {
    int l, r, id ;  
} q[N], p[N];
bool cmp( Q a, Q b ) {
    return ( a.l / tot == b.l / tot ) ? a.r < b.r : a.l < b.l ;  
}
bool cmp2( Q a, Q b ) {
    return a.l < b.l ;
}
void tr_add( int x, int k ) {
    for( int i = x; i <= n; i += lowbit(i) ) tree[i] += k ; 
}
int tr_sum( int x ) {
    int ans = 0 ;
    for( int i = x; i; i -= lowbit(i) ) ans += tree[i] ; 
    return ans ; 
}
void init() {
    sort( q + 1, q + m + 1, cmp ) ;
    int l = 1, r = 0; int Ans = 0 ;
    rep( i, 1, m ) {
        while( r < q[i].r ) Ans += 1ll * ( r - l + 1 - tr_sum(c[++ r]) ), tr_add( c[r], 1 ) ;
        while( r > q[i].r ) Ans -= 1ll * ( r - l + 1 - tr_sum(c[r --]) ), tr_add( c[r + 1], -1 ) ;
        while( l < q[i].l ) Ans -= 1ll * tr_sum( c[l ++] - 1 ), tr_add( c[l - 1], -1 ) ;
        while( l > q[i].l ) Ans += 1ll * tr_sum( c[-- l] - 1 ), tr_add( c[l], 1 ) ; 
        ans[q[i].id] = Ans ; 
    }
    rep( i, 1, m ) printf("%lld\n", ans[i] ) ;
}
signed main()
{
    n = read(), m = read() ; tot = n / sqrt( m * 1.5 ) ;
    rep( i, 1, n ) p[i].l = c[i] = read(), p[i].id = i ; 
    sort( p + 1, p + n + 1, cmp2 ) ; 
    int idnum = 0 ; p[0].l = -1 ; 
    rep( i, 1, n ) c[p[i].id] = ( idnum += ( p[i].l != p[i - 1].l ) ) ;
    rep( i, 1, m ) q[i].l = read(), q[i].r = read(), q[i].id = i ; 
    init() ; 
    return 0;
}

貌似在\(O(2)\)加持下能夠得到 \(20\) 的高分\((-///-)\)code

考慮優化,咱們發現咱們的某一次詢問實際上是能夠差分的。get

即每次右指針右移的時候,都有這樣的一組詢問:\(l-r\)中有多少數比\(a[r]\)it

然而這個東西能夠差分:\([1,r]\)中比\(a[r]\)大的數的數量\(-[1,l]\)中比\(a[r]\)大的數的數量。class

咱們發現對於前面那一坨貌似就是直接的逆序對,能夠用樹狀數組作。
複雜度\(O(nlogn)\)

而後對於後面那一坨,咱們能夠將莫隊指針的移動再次離線,將全部指針\(r\)的移動,存到其移動前對應的\(l\)\((\)開個\(vector)\)

而後咱們想要處理出後面那一坨怎麼辦,將右指針的移動再次離線後,咱們能夠考慮以相似於掃描線的方式讓左指針從\(1-n\) 過去

這樣咱們只須要在掃到的位置\(x\)時去解決以\(x\)爲左端點的右端點的移動便可。(即\(1-x\)內有多少個數比存下來的右端點大)

這個時候咱們一共存了\(n\sqrt m\) 組移動,(莫隊複雜度,總移動次數)也就是要處理\(n\sqrt m\)組詢問。

然而咱們能夠平衡它的複雜度,由於咱們要處理\(n\sqrt m\)詢問,卻只須要插入\(O(n)\)組數(即左端點掃過去的過程只移動 \(n\) 次)

可使用一個插入\(O(\sqrt n),\)查詢\(O(1)\)的數組結構\(->\)值域分塊。

對於莫隊中的四種移動分\(4\)類討論,最後須要從\(1-n\)用左端點掃一遍還須要再用右端點從\(n-1\)掃一遍。

這樣複雜度就被平衡到了\(O(n\sqrt m+n\sqrt n)\)辣。

可是這樣作要把\(n\sqrt m\)移動存下來,空間吃不下。

咱們發現其實莫隊的移動和有規律,當右端點移動的時候,其左端點並無發生任何移動,換而言只,咱們剛剛的作法其實將全部右端點的移動都存在了同一個左端點\(l\)\(vector\)內。

並且這些右端點的編號其實都是連續的,因此其實咱們根本就不必全部的移動都存下來,能夠將左端點不動的若干次右端點的移動看成一個區間存起來就好了。

固然你對於區間內的元素的詢問仍是要逐一處理的。

這樣空間複雜度就變成了 \(O(M)\)

注意,若是這樣處理最後對於每一個ans數組其實存下來的是其於上一次的左右端點之間的變化量,因此還要再作一遍前綴和。

下面是喜聞樂見的代碼\(QwQ\)

#include<bits/stdc++.h>
using namespace std;
#define LL long long
int read() {
    char cc = getchar(); int cn = 0, flus = 1;
    while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
    while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
    return cn * flus;
}
const int N = 100000 + 5 ;
const int M = 355 ; 
#define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
#define drep( i, t, s ) for( register int i = t; i >= s; -- i )
#define lowbit(x) ( x & ( - x ) )
int n, m, tot, idnum, tree[N], c[N], num1[N], num2[N], cnt, w[N], bel[N], sz, L[M], R[M], ad[M] ; 
LL ans[N], sum1[N], sum2[N];
struct Q { int l, r, id ; } q[N], p[N];
struct node { int p, l, r, id ; };
vector< node> vec1[N], vec2[N] ; 
bool cmp( Q a, Q b ) { return ( a.l / tot == b.l / tot ) ? a.r < b.r : a.l < b.l ; }
bool cmp2( Q a, Q b ) { return a.l < b.l ; }
void tr_add( int x, int k ) { for( int i = x; i <= n; i += lowbit(i) ) tree[i] += k ; }
int tr_sum( int x ) {
    int ans = 0 ;
    for( int i = x; i; i -= lowbit(i) ) ans += tree[i] ; 
    return ans ; 
}
void add1( int x, int p, int k1, int k2, int id ) { //存查詢 
    vec1[x].push_back((node){ p, k1, k2, id } ) ;
}
void add2( int x, int p, int k1, int k2, int id ) {//存查詢 
    vec2[x].push_back((node){ p, k1, k2, id } ) ;
}
void pushup1( int x ) { //左邊作的值域分塊 
    if( ad[bel[x]] ) rep( i, L[bel[x]], R[bel[x]] ) w[i] += ad[bel[x]] ; ad[bel[x]] = 0 ;
    rep( i, L[bel[x]], x ) ++ w[i];
    int siz = bel[x] - 1 ; rep( i, 1, siz ) ++ ad[i] ;
}
void pushup2( int x ) { //右邊作的值域分塊 
    if( ad[bel[x]] ) rep( i, L[bel[x]], R[bel[x]] ) w[i] += ad[bel[x]] ; ad[bel[x]] = 0 ;
    rep( i, x, R[bel[x]] ) ++ w[i] ; rep( i, bel[x] + 1, sz ) ++ ad[i] ; 
}
void solve() {
    cnt = sqrt( idnum ); sz = 1; L[sz] = 1;  
    if( cnt * cnt < idnum ) ++ cnt ; 
    rep( i, 1, idnum ) { bel[i] = sz ; if( i % cnt == 0 ) R[sz] = i, L[++ sz] = i + 1 ; } //分塊初始化 
    R[sz] = idnum ;
    rep( i, 1, n ) {
        int siz = vec1[i].size() - 1, fr, ed, id; LL p ; 
        rep( j, 0, siz ) { 
        id = vec1[i][j].id, p = 1ll * vec1[i][j].p, fr = vec1[i][j].l, ed = vec1[i][j].r; 
            rep( k, fr, ed ) ans[id] += 1ll * p * ( ad[bel[c[k] + 1]] + w[c[k] + 1] );
        } pushup1( c[i] ); //給[1,c[i]]+1 
    }
    memset( ad, 0, sizeof(ad) ), memset( w, 0, sizeof(w) ) ;
    drep( i, n, 1 ) {
        int siz = vec2[i].size() - 1, fr, ed, id; LL p ;
        rep( j, 0, siz ) { 
        id = vec2[i][j].id, p = 1ll * vec2[i][j].p, fr = vec2[i][j].l, ed = vec2[i][j].r; 
            rep( k, fr, ed ) ans[id] += 1ll * p * ( ad[bel[c[k] - 1]] + w[c[k] - 1] );
        } pushup2( c[i] ) ;//給 [c[i],n]+1 
    }
}
signed main()
{
    n = read(), m = read() ; tot = 355 ;
    rep( i, 1, n ) p[i].l = c[i] = read(), p[i].id = i ; 
    sort( p + 1, p + n + 1, cmp2 ), p[0].l = -1 ; 
    rep( i, 1, n ) c[p[i].id] = ( idnum += ( p[i].l != p[i - 1].l ) ) ; //離散化 
    rep( i, 1, m ) q[i].l = read(), q[i].r = read(), q[i].id = i ;  //存查詢 
    rep( i, 1, n ) num1[i] += ( i - 1 - tr_sum(c[i]) ), tr_add( c[i], 1 ), sum1[i] = sum1[i - 1] + num1[i] ; //從左往右作 
    memset( tree, 0, sizeof(tree) ) ; 
    drep( i, n, 1 ) num2[i] += tr_sum(c[i] - 1), tr_add( c[i], 1 ), sum2[i] = sum2[i + 1] + num2[i] ;//從右往左作 
    sort( q + 1, q + m + 1, cmp ) ;
    int l = 1, r = 0;
    rep( i, 1, m ) {
        if( r < q[i].r ) ans[q[i].id] += ( sum1[q[i].r] - sum1[r] ), 
            add1( l, -1, r + 1, q[i].r, q[i].id ), r = q[i].r ;
        if( r > q[i].r ) ans[q[i].id] -= ( sum1[r] - sum1[q[i].r] ), 
            add1( l, 1, q[i].r + 1, r, q[i].id ), r = q[i].r ;
        if( l < q[i].l ) ans[q[i].id] -= ( sum2[l] - sum2[q[i].l]), 
            add2( r, 1, l, q[i].l - 1, q[i].id ), l = q[i].l ;
        if( l > q[i].l ) ans[q[i].id] += ( sum2[q[i].l] - sum2[l] ), 
            add2( r, -1, q[i].l, l - 1, q[i].id ), l = q[i].l ;
    }
    solve() ; 
    rep( i, 1, m ) ans[q[i].id] += ans[q[i - 1].id] ; rep( i, 1, m ) printf("%lld\n", ans[i] ) ;
    return 0;
}
相關文章
相關標籤/搜索