小奇的數列

題目

給定一個長度爲 N 的數列 A,以及 Q 次詢問,每次給出三個數 LR P,詢問 (A[L']
+ A[L'+1] + + A[R']) mod P 的最小值。其中 L <= L' <= R' <= R
即模意義下的區間子串和的最小值。
輸入格式:
第一行包含兩個正整數 N Q,表示數列的長度和詢問的個數。
第二行爲 N 個整數,爲 A[1]..A[N]
接下來 Q 行,每行三個數 LR P,代表一次詢問。
輸出格式:
對於每次詢問,輸出一行一個整數表示要求的結果。
輸入樣例:4 2
8 15 9 9
1 3 10
1 4 17
輸出樣例:
2
1
數據範圍:
對於 40%的數據 N<=500M<=4000
對於 100%的數據 N<=10000M<=100001<=A[i]<=10^9, 1 <= P <= 10000左右
 
限時3s
 

題解

先求一個前綴和

首先由抽屜原理可以得:

如果R-L+1>=P,則可以直接輸出0

因爲一定有兩個前綴和在P的模意義下是相等的

然後把(sum[l] - sum[l-1]) % P 到 (sum[r] - sum[l-1]) % P依次放進平衡樹中,在插入前求它的前驅,兩者相減後,即使對於當前點爲右端點所得到的最小子串和

這樣也不會再去取模了

 

感覺自己的數據結構的題做得太少,可能還需要一定的積累,記得解題的方法很重要

 

代碼

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAXN = 1e6 +3;
int ch[MAXN][2] , val[MAXN] , cnt[MAXN] , siz[MAXN] , fa[MAXN];
int root , ncnt;
int chk( int x ){
    return ch[fa[x]][1] == x;
}
void pushup( int x ){
    siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + cnt[x];
}
void rorate( int x ){
    int f = fa[x] , ff = fa[f] , w = chk( x );
    ch[f][w] = ch[x][w^1];
    fa[ch[x][w^1]] = f;
    ch[ff][chk(f)] = x;
        fa[x] = ff;
    ch[x][w^1] = f;
        fa[f] = x;
    pushup( f );pushup( x );
}
void splay( int x , int goal ){
    while( fa[x] != goal ){
        int f = fa[x] , ff = fa[f];
        if( ff != goal ){
            if( chk( x)  == chk(f ) ) rorate( f );
            else
                rorate( x );
        }
        rorate( x );
    }
    if( !goal ) root = x;
}
void find_( int x ){
    if( !root ) return ;
    int cur = root;
    while( ch[cur][val[cur] < x] && val[cur] != x ){
        cur = ch[cur][val[cur] < x];
    }
    splay( cur , 0 );
}
void insert_( int x ){
    int cur = root , p = 0;
    while( cur && val[cur] != x ){
        p = cur;
        cur = ch[cur][val[cur] < x];
    }
    if( cur ){
        cnt[cur] ++;
    }
    else{
        cur = ++ncnt;
        if( p ) ch[p][x>val[p]] = cur;
        ch[cur][0] = ch[cur][1] = 0;
        val[cur] = x;cnt[cur] = 1;
        siz[cur] = 1;fa[cur] = p;
    }
    splay( cur , 0 );
}
int kth( int k ){
    int cur = root;
    while( true ){
        if( siz[ch[cur][0]] >= k && ch[cur][0] ){
            cur = ch[cur][0];
        }
        else if( siz[ch[cur][0]] + cnt[cur] < k ){
            k -= siz[ch[cur][0]] + cnt[cur];
            cur = ch[cur][1];
        }
        else
            return cur;
    }
}
int pre( int x ){
    find_( x );
    if( val[root] <= x ) return root;
    int cur = ch[root][0];
    while( ch[cur][1] ){
        cur = ch[cur][1];
    }
    return cur;
}
int n , Q , A[MAXN] , sum[MAXN];
void read( int &x ){
    x = 0;char s = getchar();
    while( s < '0' || s > '9' ) s = getchar();
    while( s >= '0' && s <= '9' ){
        x = x * 10 + s -'0';
        s = getchar();
    }
}
int main()
{
    freopen( "array.in" , "r" , stdin );
    freopen( "array.out" , "w" , stdout );
    read( n );read(Q);
    for( int i = 1 ; i <= n ; i ++ ){
        read( A[i] );
        sum[i] = sum[i-1] + A[i];
    }
    while( Q -- ){
        int L , R , P;
        read( L );read( R );read( P );
        if( R - L + 1 >= P ){
            printf( "0\n" );
            continue;
        }
        for( int i = 0 ; i <= P ; i ++ ){
            fa[i] = val[i] = ch[i][0] = ch[i][1] = siz[i] = cnt[i] = 0;
        }
        root = 0;ncnt = 0;
        int ans = A[L] % P;
        for( int i = L ; i <= R ; i ++ ){
            int tot = ( sum[i] - sum[L-1] ) % P;
            if( i == L )
                insert_( tot );
            else{
                int j = pre( tot );
                ans = min( ans , tot - val[j] );
                insert_( tot );
            }
        }
        printf( "%d\n" , ans );
    }
    return 0;
}

 

發佈了70 篇原創文章 · 獲贊 7 · 訪問量 4134
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章