挣扎了很久还是写了出来,吐血三升
一个不算难的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;
}