一维前缀和
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],s[N];
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
while(m--){
int l,r;
cin>>l>>r;
printf("%d\n",s[r]-s[l-1]);
}
return 0;
}
二维前缀和
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int a[N][N],s[N][N];
int n,m,q;
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
}
}
while(q--){
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
printf("%d\n",s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]);
}
return 0;
}
K倍区间
思路分析
暴力解法:先生成一维前缀和数组,然后两层循环遍历前缀和数组,(s[r]-s[l-1])%k==0使cnt++,时间复杂度O(n2)
继续分析,如果某个连续序列和为k的倍数,即(s[r]-s[l-1])%k=0,那么s[r]和s[l-1]对k的余数相等,每两个对k取余数相等的区段和都能组合成一个区间。
(acwing题解上的例子,帮助理解)
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
typedef long long LL;
LL ans;
LL cnt[N];
LL a[N],s[N];
int main(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
for(int i=1;i<=n;i++){
ans+=cnt[s[i]%k]; //余数为s[i]%k的这个区段和与前边的所有余数相同的区段都组合成一个答案所以更新
cnt[s[i]%k]++;//同时,余数为s[i]%k的区段加1
}
printf("%lld\n",ans+cnt[0]);//cnt[0]表示整除k的区段,这样的区段自身也是一个答案
return 0;
}
数的范围(整数二分)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int n,m;
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
for(int i=0;i<m;i++){
int x;
scanf("%d",&x);
int l=0,r=n-1;
while(l<r){
int mid=(l+r)/2;
if(a[mid]>=x){
r=mid;
}else{
l=mid+1;
}
}
if(a[r]==x) printf("%d ",l);
else printf("-1 ");
l=0,r=n-1;
while(l<r){
int mid=(l+r+1)/2;
if(a[mid]<=x){
l=mid;
}else{
r=mid-1;
}
}
if(a[r]==x) printf("%d\n",r);
else printf("-1\n");
}
return 0;
}
分巧克力
思路分析
采用二分算法实现最小值最大化。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int h[N],w[N];
int n,k;
bool check(int x){
int sum=0;
for(int i=0;i<n;i++){
sum+=(h[i]/x)*(w[i]/x);
}
return sum>=k;
}
int main(){
cin>>n>>k;
for(int i=0;i<n;i++) cin>>h[i]>>w[i];
int l=0,r=1e5+10;
while(l<r){
int mid=(l+r+1)/2;
if(check(mid)){
l=mid;
}else{
r=mid-1;
}
}
printf("%d",l);
return 0;
}
砍树
思路分析
同上题
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
typedef long long LL;
LL a[N];
LL n,m;
bool check(LL x){
LL tol=0;
for(int i=0;i<n;i++){
if(a[i]>=x) tol+=(a[i]-x);
}
return tol>=m;
}
int main(){
cin>>n>>m;
LL sum=0;
LL l,r;
for(int i=0;i<n;i++){
cin>>a[i];
r=max(r,a[i]);
l=0;
}
while(l<r){
int mid=(l+r+1)/2;
if(check(mid)){
l=mid;
}else{
r=mid-1;
}
}
printf("%lld",l);
return 0;
}
木材加工
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int n,k;
bool check(int x){
int cnt=0;
for(int i=0;i<n;i++){
cnt+=(a[i]/x);
}
return cnt>=k;
}
int main(){
cin>>n>>k;
int l,r;
l=0,r=0;
for(int i=0;i<n;i++){
cin>>a[i];
r=max(r,a[i]);
}
while(l<r){
int mid=(l+r+1)/2;
if(check(mid)){
l=mid;
}else{
r=mid-1;
}
}
printf("%d",l);
return 0;
}
愤怒的牛
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int n,m;
bool check(int x){
int cnt=1; //表示事先放入一头牛
int now=0; //表示现在放牛的编号
for(int i=1;i<n;i++){
if(a[i]-a[now]>=x){
cnt++;
now=i;
}
}
return cnt>=m;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>a[i];
}
sort(a,a+n);
int l=0,r=a[n-1];
while(l<r){
int mid=(l+r+1)/2;
if(check(mid)){
l=mid;
}else{
r=mid-1;
}
}
printf("%d",l);
return 0;
}
数列分段二
思路分析
最大值最小化。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int l,r;
int n,m;
bool check(int x){
int val=0;
int cnt=1;
for(int i=0;i<n;i++){
if(val+a[i]<=x) val+=a[i]; //此时表示可以继续分到该段
else{
val=a[i]; //以这个数字作为下一段的开始
cnt++; //段数加一
}
}
return cnt<=m;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>a[i];
l=max(l,a[i]);
r+=a[i];
}
while(l<r){
int mid=(l+r)/2;
if(check(mid)){
r=mid;
}else{
l=mid+1;
}
}
printf("%d",l);
return 0;
}
跳石头
思路分析
最大值最小化
#include<bits/stdc++.h>
using namespace std;
const int N=50001;
int a[N];
int m,n,k;
bool check(int x);
int main(){
scanf("%d%d%d",&m,&n,&k);
a[0]=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
a[n+1]=m;
//sort(a,a+n+2);
int l,r,mid;
l=0;
r=a[n+1];
while(l<r){
mid=(l+r)/2;
if(check(mid)){
r=mid;
}else{
l=mid+1;
}
}
printf("%d",l);
return 0;
}
bool check(int x){
int cnt=0;
int now=0;
for(int i=1;i<=n+1;i++){
if(a[i]-a[now]<=x){
cnt++;
}else{
now=i;
}
}
if(cnt>k) return 1;
else return 0;
}
路标设置
思路分析
最小值最大化
#include<bits/stdc++.h>
using namespace std;
const int N=100001;
int a[N];
int m,n,k;
bool check(int x);
int main(){
scanf("%d%d%d",&m,&n,&k);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
int l,r,mid;
l=0;
r=m;
while(l<r){
mid=(l+r+1)/2;
if(check(mid)){
l=mid;
}else{
r=mid-1;
}
}
printf("%d",l+1);
return 0;
}
bool check(int x){
int m=0;
for(int i=1;i<=n;i++)
if(a[i]-a[i-1]>=x)
{
m+=(a[i]-a[i-1])/x;
if((a[i]-a[i-1])%x==0)
m--;
}
if(m>k)
return 1;
return 0;
}
牛的学术圈Ⅰ
思路分析 最小值最大化
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int c[N];
int t,L;
bool check(int x){
int y=L;
int cnt=0;
for(int i=0;i<t;i++){
if(c[i]>=x){
cnt++;
}else if(c[i]+1>=x&&y){
cnt++;
y--;
}
}
return cnt>=x;
}
int main(){
int l=0,r=N;
cin>>t>>L;
for(int i=0;i<t;i++){
cin>>c[i];
}
while(l<r){
int mid=(l+r+1)/2;
if(check(mid)){
l=mid;
}else{
r=mid-1;
}
}
printf("%d",l);
return 0;
}