AOJ 0254 : Scone スコーン配達計画
問題リンク:Scone | Aizu Online Judge
問題概要:
0以上の整数からなる数列と正の整数mが与えられる
数列の連続する部分列の総和%mの最大値を求めよ
解法:
BIT + 二分探索した(一時期は全探索で通った)
数列の累積和を求め、それらをmで割った余りがそれぞれどの位存在するかを数える
例えば、
m = 10 で 数列が { 2,3,7,8} なら
累積和をとると 2 5 12 20 となり、
mでmodをとると 2 5 2 0 となる
ここから
0 1 2 3 4 5 6 ... ( index )
0 0 2 2 2 3 3 ... ( value ) <= これをBITで作成
入力で与えられた数列を0番目から昇順に、そこを最も左とする部分列の総和%mの最大値を求める
これは二分探索でもとまる
普通は 0 から m-1 のなかで存在するものの最大値を求めればよいが、
今回は 数列の0番からの累積和になっているので、
[0,m-1]ではなく[(ここまでの総和)%m,(m-1 + (ここまでの総和)%m ) %m] の中で存在するものの最大値を求める
modをとっているので左の値 > 右の値とある場合があるので
そのときは範囲を2つに分ければ良い
余談:
二分探索がかけるようになりました ※ while(L+1
4 10 2 3 7 8 0 0
Sample Output 01:
8
Sample Input 02:
151 74391 10 10 8 10 3 11 1 11 5 2 5 8 11 4 2 7 2 5 6 8 4 7 7 6 1 1 4 4 5 6 4 3 5 9 10 5 8 10 4 10 10 8 4 7 10 5 1 1 11 9 5 5 11 10 9 5 10 9 8 3 11 11 4 3 4 10 1 8 6 9 10 4 5 4 7 11 11 9 10 8 4 2 1 3 11 7 7 7 3 1 7 2 10 8 4 2 6 3 8 11 9 4 1 2 5 5 10 3 2 8 10 6 7 9 6 6 4 11 11 6 9 6 6 7 1 7 7 6 9 1 4 4 2 2 3 6 7 1 8 6 9 7 9 2 2 4 8 5 1 5 9 9 0 0
Sample Output 02:
942
Sample Input 03:
3 5 1 2 3 0 0
Sample Output 03:
3
コード:
#include<bits/stdc++.h> #define REP(i,s,n) for(int i=s;i<n;i++) #define rep(i,n) REP(i,0,n) using namespace std; typedef long long ll; const int MAX_N = 31000; const int MAX_M = 100010; int n,m,arr[MAX_N],mod_sum[MAX_N]; int bit[MAX_M]; int sum(int i) { ++i; int s = 0; while( i > 0 ){ s += bit[i]; i -= i & -i; } return s; } void add(int i,int x){ ++i; while( i <= m ) { bit[i] += x; i += i & -i; } } int getMax(int L,int R){ int target = sum(R); rep(_,65){ int M = ( L + R ) / 2; if( sum(M) >= target ) R = M; else L = M; } if( L != R ) { int res = -1; if( sum(R) >= target ) res = R; if( sum(L) >= target ) res = L; assert( res != -1 ); R = res; } if( R == 0 && sum(R) ) return 0; else if( R == 0 ) return -1; if( sum(R) - sum(R-1) ) return R; return -1; } inline int convert(int value,int sub){ return ( value - sub + m ) % m; } void compute(){ int maxi = 0; rep(i,n) if( arr[i] ) add(mod_sum[i],1), maxi = max(maxi,arr[i]); if( maxi == m-1 ) { printf("%d\n",maxi); return; } int sub = 0; rep(i,n) if( arr[i] ) { int low = sub; int high = ( m - 1 + sub ) % m; if( low < high ) { int temp = getMax(low,high); if( temp != -1 ) maxi = max(maxi,convert(temp,sub)); } else { int temp = getMax(low,m-1); if( temp != -1 ) maxi = max(maxi,convert(temp,sub)); temp = getMax(0,high); if( temp != -1 ) maxi = max(maxi,convert(temp,sub)); } ( sub += arr[i] ) %= m; add(mod_sum[i],-1); if( maxi == m-1 ) break; } printf("%d\n",maxi); } int main(){ while( scanf("%d %d",&n,&m), n|m ){ memset(bit,0,sizeof(bit)); rep(i,n) { scanf("%d",arr+i); arr[i] %= m; if( i ) mod_sum[i] = ( mod_sum[i-1] + arr[i] ) % m; else mod_sum[i] = arr[i]; } compute(); } return 0; }