【算法2-1】前缀和、差分与离散化

P8218 【深进1.例1】求区间和

思路:

               模板题

代码:

#include<iostream>
using namespace std;

const int N=1e5+10;
int a[N];
int s[N];

int main()
{
    int n,m;
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        cin>>a[i];
        s[i]=s[i-1]+a[i];
    }
    cin>>m;
    while (m--)
    {
        int l,r;
        cin>>l>>r;
        cout<<s[r]-s[l-1]<<endl;
    }
    return 0;
}

P1719 最大加权矩形

思路:

        保存数值之后,求前缀和,之后求用两个指针模拟每个点,求每个点到两个指针的区间和,比大小即可

代码:

        时间复杂度o(n^4)

#include <iostream>
using namespace std;
const int N=130;
int map[N][N];
int s[N][N];
int ans;

int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            cin>>map[i][j];
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+map[i][j];
        }
        
       for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            for(int q=i;q<=n;q++)
                for(int p=j;p<=n;p++)
                {
                 int temp=s[q][p]-s[i-1][p]-s[q][j-1]+s[i-1][j-1];
                 ans=max(ans,temp);
                }
        }
        cout<<ans<<endl;
    return 0;
}

P1314 [NOIP2011 提高组] 聪明的质监员

思路:

        因为,W的值越大,所选择的范围越小,所以Y越小,W值越小,所选择的范围越大,所以Y越大,所以我们就会得到一个关系W和Y之间的一个关系,因为要求|s-y|的最小值,所以根据Y与s的值,确定W。

        所以,根据这个关系,以w的最大值+1与w的最小值为范围(因为,当W为最小的时候,所有石头都可以选,Y最大,当W为最大的时候,应该是所有石头都不能选,此时Y最小,所以我们要保证能二分出来,W的最小和W的最大,也就是w最大值+1),二分出来一个W, 之后用这个W在计算|s-y|的值

        需要注意的是,因为我们求的值是绝对值,所以二分的方法,就会特别讲究,如果不存在y==s的值的话,两个模板二分出来的就会是大于最合适的W的第一个值,或小于最合适W的第一个值,这是就需要比较那个距离W的位置近,但这样的话就会TLE,所以我们最好的解决方法就是一个模板在查找的过程中比较,最后输出比较出来的值就行。

以下是两种模板分别二分出来的结果:

从右向左找

        

 从左向右找

AC代码:

        时间复杂度o(n)

#include <iostream>
using namespace std;
const int N=1e6+10;
int  w[N],v[N];
long long  sw[N],sv[N];
long long  ans=0x3f3f3f3f3f3f3f3f;
long long sum=0x3f3f3f3f3f3f3f3f;
int  maxw,minw=0x3f3f3f3f;
int l1[N],r1[N];
int n,m;
long long s;
long long check(long long W)
{
    for(int i=1;i<=n;i++)
    {
        if(w[i]>=W) 
        {
            sw[i]=sw[i-1]+1;
            sv[i]=sv[i-1]+v[i];
        }
        else 
        {
            sw[i]=sw[i-1];
            sv[i]=sv[i-1];
        }
    }
    long long y=0;
    for(int i=0;i<m;i++)
    {
        int l=l1[i],r=r1[i];
        y+=(sw[r]-sw[l-1])*(sv[r]-sv[l-1]);
    }
    sum=llabs(s-y);
    return y;
}

int main()
{
    scanf("%d%d%lld",&n,&m,&s);
    
    for(int i=1;i<=n;i++) 
    {
        scanf("%d%d",&w[i],&v[i]);
        maxw=max(maxw,w[i]);
        minw=min(minw,w[i]);
    }
    maxw++;
    for(int i=0;i<m;i++)
    {
        scanf("%d %d",&l1[i],&r1[i]);
    }
    
    long long l=minw,r=maxw;
    while (l<r)
    {
        long long mid=l+r+1>>1;
        if(check(mid)>=s)l=mid;
        else r=mid-1;
        if(sum<ans) ans=sum;
    }
    cout<<ans<<endl;
    return 0;
}

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值