Codeforces862E(函数处理,stl?)

27 篇文章 0 订阅
1 篇文章 0 订阅

挣扎了很久还是写了出来,吐血三升
一个不算难的E题,题解简单易懂,是我太菜了QAQ
题意:输入n,m,q(1 ≤ n ≤ m ≤ 10^5, 1 ≤ q ≤ 10^5),存在一个j(0<=j<=m-n),使得这里写图片描述(懒到不想输公式…) 最小,并输出这个最小值。有q次操作,对于每一次操作,都给出l,r,x。分别代表对a数组操作的左边界右边界以及修改值。在每一次操作过后都输出f(j)的最小值。
思路:虽然tag里面有打二分,然而菜鸡如我并没有看出二分的点在哪里…
首先这个恶心巴拉的公式,肯定不能直接这样操作,对其进行拆分,得到这里写图片描述
由于b数组是不变的,所以可以提前处理

  long long tempb=0;
    for(int i=0;i<n;i++){
        scanf("%d",&b[i]);
        tempb+=b[i]*reverse(i+1);
    }
    s.insert(tempb);
    for(int i=n;i<m;i++){
        scanf("%d",&b[i]);
       // tempb=(tempb-b[i-n])*(-1)+b[i]*reverse(n);
        tempb+=b[i-n];
        tempb=-tempb;
        tempb+=reverse(n)*b[i];
        s.insert(tempb);
    }

这里有个神奇的事情…
tempb=(tempb-b[i-n])*(-1)+b[i]*reverse(n);

        tempb+=b[i-n];
        tempb=-tempb;
        tempb+=reverse(n)*b[i];

的处理结果是不一样的。选用后者的原因先留个空(
绝对值在外部,且求的是总和,用不到线段树什么的…直接求A数列在这条公式中的和就可以了

 for(int i=0;i<n;i++){
        int a;
        scanf("%d",&a);
        suma+=a*reverse(i);
    }

对于q次修改,我们容易知道当修改数字个数为偶数时,修改对f(j)的最小值毫无影响(全都互相抵消了),此处需要注意的是,修改数字的个数是r-l+1而非r-l。当l为偶数时,a的总和中减去x,为奇数时加上x

 if((r-l)%2==0){
            if(l%2==0) suma-=x;
            else suma+=x;
        }

最重要的一部分,如何求解。显然,要想使得总值最小,我们要找到一个尽可能接近suma的序列。故用set来存储b中每一个长度为n的序列因其自带排序(好像有点描述不清…),lower_bound(suma)来查找最接近的一个值
注意:set的最后一位不是一个实值,而是一个结束符,所以要是查到s.end()的话,迭代器需要前推一格
最接近的也有可能是lower_bound(suma)前面的那个值,所以比较时需要比较两个值。

set<long long>s;
long long ans(long long sum){
    set<long long>::iterator it=s.lower_bound(sum);
    if(it==s.end()) it--;
    long long re=abslong((*it)-sum);
    if(it!=s.begin()) it--;
    return min(re,abslong((*it)-sum));
}

附全代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,q;
int reverse(int x){
    if(x%2==0) return 1;
    return -1;
}
long long abslong(long long x){
    if(x>0) return x;
    return -x;
} 
int b[100007];
set<long long>s;
long long ans(long long sum){
    set<long long>::iterator it=s.lower_bound(sum);
    if(it==s.end()) it--;
    long long re=abslong((*it)-sum);
    if(it!=s.begin()) it--;
    return min(re,abslong((*it)-sum));
}
int main(){
    scanf("%d%d%d",&n,&m,&q);
    long long suma=0;
    for(int i=0;i<n;i++){
        int a;
        scanf("%d",&a);
        suma+=a*reverse(i);
    }
    long long tempb=0;
    for(int i=0;i<n;i++){
        scanf("%d",&b[i]);
        tempb+=b[i]*reverse(i+1);
    }
    s.insert(tempb);
    for(int i=n;i<m;i++){
        scanf("%d",&b[i]);
       // tempb=(tempb-b[i-n])*(-1)+b[i]*reverse(n);
        tempb+=b[i-n];
        tempb=-tempb;
        tempb+=reverse(n)*b[i];
        s.insert(tempb);
    }
    cout<<ans(-suma)<<endl;
    while(q--){
        int l,r,x;
        scanf("%d%d%d",&l,&r,&x);
        if((r-l)%2==0){
            if(l%2==0) suma-=x;
            else suma+=x;
        }
        cout<<ans(-suma)<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值