鼠鼠我呀
题目描述
在一个叫做酱西功爷枝叶鸡树学院的地方有n只小动物,要么是鼠鼠,要么是鸭鸭,从1到n编号,每只小动物有个体重 a i a_i ai。
在这个学校里,存在一种神奇的魔法,可以将编号位于某个区间 [ l , r ] [l,r] [l,r]内的所有鼠鼠都变为鸭鸭,鸭鸭都变为鼠鼠(魔法并不会改变体重)。
现在你可以施放这个魔法至多1次。(也可以不施放)
问最终鸭鸭的总重量最多是多少?
输入格式
第一行一个整数T表示样例个数。 ( 1 ≤ T ≤ 10 ) (1≤T≤10) (1≤T≤10)
对于每个样例:
第一行一个整数n表示小动物的个数。( 1 ≤ n ≤ 1 0 5 1≤n≤10^5 1≤n≤105)
第二行 n n n个整数,表示第 i i i个小动物的类型。0表示鼠鼠,1表示鸭鸭。
第三行 n n n个整数,表示第 i i i个小动物的体重 a i a_i ai。( 1 ≤ a i ≤ 1 0 9 1≤a_i≤10^9 1≤ai≤109)
输出格式
对于每个样例一行一个整数表示答案。
样例输入
2
3
0 0 0
1 2 3
4
0 1 0 0
2 5 6 5
样例输出
6
16
题目分析
假设原本所有鸭鸭的重量是sum,操作后形成的偏移是fix,即经过至多1次操作后鸭鸭的总重量是sum+fix,这里的fix最小是0,因为如果操作后的fix是负数,会使得鸭鸭的总重量减少,倒不如不操作,直接是sum就可以了。
来看这个fix应该怎么求。以样例的第二组数据为例,如果操作区间是[0,4],那么每个位置对应的偏移量分别为2,-5,6,5。总的偏移量为2-5+6+5=8。如果操作区间是[1,2],总的偏移量为-5+6=1。其实要求某个区间的偏移量就是数组[2,-5,6,5]对应区间的区间和。这里的[2,-5,6,5]也叫做偏移量数组,因为对于位置i的值而言,如果他是鸭鸭,一次操作后就变为了鼠鼠,那么之前累加上的他的重量应该减去,所以偏移量就是负的该位置的重量也就是-a[i],如果他是鼠鼠,一次操作后就变为了鸭鸭,他的重量应该累加上,所以偏移量就是该位置的重量也就是a[i]。所以我们可以求一下偏移量数组的前缀和,然后利用前缀和求最大的偏移量区间和,也就是一开始说的fix,累加到原始的重量和sum里面就好。
假设偏移量的前缀和数组是pre[i],那么偏移量的区间和[l,r]就用pre[r]-pre[l-1],如果我们遍历l和r的话,双层嵌套for循环会超时,所以这里应该采用类似双指针的做法,假设固定了r,要想pre[r]-pre[l-1]最大,需要pre[l-1]是区间[1,l-1]里面最小的那个数,用mi表示,关键代码如下,
fix=0;mi=0;
for(int i=1;i<=n;i++)
{
fix=max(fix,s[i]-mi);//i固定时,mi是下标比i小的数中的最小值
mi=min(mi,s[i]);
}
fix最小是0,所以初始化为0。这里的mi最小值也为0,mi为0时表示的是区间[1,i]的区间和,如果mi不为负数,说明s[i]-mi会变小,即比s[i]本身小,倒不如直接用s[i],s[i]表示的就是前i项之和,也是区间和。
题目代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll g[N],w[N],s[N],p[N];
ll sum,fix,mi;
int main(){
int t;cin>>t;
while(t--){
int n;cin>>n;
for(int i=1;i<=n;i++)cin>>g[i];
for(int i=1;i<=n;i++)cin>>w[i];
for(int i=1;i<=n;i++)
{
s[i]=s[i-1]+(g[i] == 1 ? -1 : 1)*w[i]; //计算出施加魔法后的偏移值(鸭子重量),如果原来是1(鸭子)就要减去,原来是0(鼠)就加上
}
sum=0;
for(int i=1;i<=n;i++) {
sum+=w[i]*g[i];
}
fix=0,mi=0;
for(int i=1;i<=n;i++)
{
fix=max(fix,s[i]-mi);
mi=min(mi,s[i]);
}
cout<<sum+fix<<'\n';
}
return 0;
}
代码有一个易错点,因为变量都是全局变量,所以sum,fix,e在每一组测试样例中都要重置为初始值。
package 前缀和和差分;
import java.util.Scanner;
public class 鼠鼠我鸭 {
static int N=(int) (1e5+10);
static long[] g=new long[N],w=new long[N],s=new long[N],p=new long[N];
static long sum,fix,mi;
public static void main(String[] args) {
int t;
Scanner scanner = new Scanner(System.in);
t = scanner.nextInt();
while(t-- > 0){
int n;
n = scanner.nextInt();
for(int i=1;i<=n;i++)g[i]=scanner.nextLong();
for(int i=1;i<=n;i++)w[i]=scanner.nextLong();
for(int i=1;i<=n;i++)
{
s[i]=s[i-1]+(g[i] == 1 ? -1 : 1)*w[i]; //计算出施加魔法后的偏移值(鸭子重量),如果原来是1(鸭子)就要减去,原来是0(鼠)就加上
}
sum=0;
for(int i=1;i<=n;i++) {
sum+=w[i]*g[i];
}
fix=0;mi=0;
for(int i=1;i<=n;i++) //从第一个遍历到最后,计算出最大最小值
{
fix=Math.max(fix,s[i]-mi);
mi=Math.min(mi,s[i]);
}
System.out.println(sum+fix);
}
}
}