题目大意是找最小的m使得前m段中每一段的最大值相加严格大于k,每一段长度为[n/m](n/m向下取整,多余的后半部分部分n-m*[n/m]不要)
先给一段我一开始的思路,和网上许多题解思路一样,但其实是有错误的。二分答案+RMQ (RMQ部分也可以用线段树)
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5 + 5; const int inf = 0x3f3f3f3f; int f[maxn][30]; int n, k, m, v[maxn]; int mx, mi, tot; inline int max( int a,int b ){ return a>b ? a:b; } inline int min( int a, int b ){ return a<b ? a:b; } inline void ST_make(){ for( int i=1; i<=n; i++ ) f[i][0] = v[i]; for( int j=1; (1<<j)<=n; j++ ) for( int i=1; i<=n; i++ ) f[i][j] = max( f[i][j-1], f[i+(1<<(j-1))][j-1] ); } inline int ST_query( int l, int r ){ int k = log(r-l+1)/log(2); return max( f[l][k], f[r-(1<<k)+1][k]); } int main(){ while( ~scanf("%d%d", &n, &k) ){ if( n<0 && k<0 ) break; memset( f, 0, sizeof(f) ); mx = -inf; mi = inf; tot = 0; for( int i=1; i<=n; i++ ){ scanf("%d", &v[i]); tot += v[i]; mx = max( v[i], mx ); mi = min( v[i], mi ); } if( mx>k ) {puts("1 "); continue;} else if( tot<=k ) {puts("-1 "); continue;} ST_make(); mx = mx ? mx:1; mi = mi ? mi:1; int l = k/mx, r = min( n, k/mi+1 ), sum; //判断上下界,节省时间 while( l<r ){ sum = 0; int mid = l+r>>1; //这个地方不能直接用m = l+r>>1 int t = n/mid; int tmp = 1; int d = t*mid; while( tmp<d){ sum += ST_query( tmp, tmp+t-1 ); tmp += t; } if( sum>k ){ //符合条件再令m = mid; r = mid; m = mid; } else l = mid+1; } printf("%d ", m); } return 0; }
思路都写好了,然鹅是错的。。。
给一份Discuss里dalao给的卡二分的数据
10 1500
1 1 1 1 1000 1000 1 1 1 1
很明显 答案是2 但是输出我记得好像是7
所以换了暴力枚举的思路,上代码
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5 + 5; const int inf = 0x3f3f3f3f; int f[maxn][30]; int n, k, m, v[maxn]; int mx, mi, tot; inline int max( int a,int b ){ return a>b ? a:b; } inline int min( int a, int b ){ return a<b ? a:b; } inline void ST_make(){ for( int i=1; i<=n; i++ ) f[i][0] = v[i]; int t = log(n)/log(2)+1; for( int j=1; j<t; j++ ) for( int i=1; i+(1<<j)-1<=n; i++ ) f[i][j] = max( f[i][j-1], f[i+(1<<(j-1))][j-1] ); } inline int ST_query( int l, int r ){ int k = log(r-l+1)/log(2); return max( f[l][k], f[r-(1<<k)+1][k]); } int main(){ while( ~scanf("%d%d", &n, &k) ){ if( n<0 && k<0 ) break; mx = -inf; mi = inf; tot = 0; for( int i=1; i<=n; i++ ){ scanf("%d", &v[i]); tot += v[i]; mx = max( v[i], mx ); mi = min( v[i], mi ); } if( tot<=k ){ printf("-1 "); continue ; } ST_make(); mx = mx ? mx:1; mi = mi ? mi:1; int l = k/mx, r = (k/mi+1, n); //判定上下界,节省时间(好像判了上界r还慢了。。) if(l==0) l = 1; bool ok = 0; for( m=l; m<=r; m++ ){ int sum = 0; int t = n/m; int d = t*m; for( int i=1; i<=d; i+=t ) //这个地方要等于号,可举例n = m时 若i<d为判定条件则不能进行,卡了一年 sum += ST_query( i, i+t-1 ); if( sum>k ){ ok = 1; break; } } if(ok) printf("%d ", m); else printf("-1 "); } return 0; }