小奇的数列

题目

给定一个长度为 N 的数列 A ,以及 Q 次询问,每次给出三个数 L R P ,询问 (A[L']
+ A[L'+1] + + A[R']) mod P 的最小值。其中 L <= L' <= R' <= R
即模意义下的区间子串和的最小值。
输入格式:
第一行包含两个正整数 N Q ,表示数列的长度和询问的个数。
第二行为 N 个整数,为 A[1]..A[N]
接下来 Q 行,每行三个数 L R P ,代表一次询问。
输出格式:
对于每次询问,输出一行一个整数表示要求的结果。
输入样例: 4 2
8 15 9 9
1 3 10
1 4 17
输出样例:
2
1
数据范围:
对于 40% 的数据 N<=500 M<=4000
对于 100% 的数据 N<=10000 M<=10000 1<=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;
}

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页