题意:
给定一个序列a[0],a[1]….a[n-1]和一个整数k,问是否有这样的两个下标使得 NP-Sum(i,j)=k,这里 NP-Sum(i,j)=a[i]-a[i+1]+a[i+2]-…+(-1)^(j-1)*a[j]。
解析:
首先题目提示说要用快速输入,如果不用快速输入可能会超时。
现在来讨论一下这题的做法:
我们可以维护这个序列的后缀和,然后枚举sum[i]查看在set表中是否存在sum[j]=sum[i]-k。
这里要分成i为奇数时的计算和偶数时的讨论,因为我们只维护一次sum的值,或+-+-+的形式,或-+-+-的形式。
在这里你可能会问为什么要维护的是后缀和?
因为我们是根据i为开头,确定这个 NP - Sum(i,j)的正负,而维护前缀和的话,此时i就变成这个序列的末尾,这样不能确定正负。
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <set>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 10;
set<ll> st;
ll a[N], k;
int n;
inline void read(ll &x) {
int flag = 0;
x = 0;
char c = getchar();
if(c == '-')
flag = 1;
while(c < '0' || c > '9') {
if(c == '-')
flag = 1;
c = getchar();
}
while(c >= '0' && c <= '9')
x = x * 10 + c - '0', c = getchar();
if(flag) x = -x;
}
bool judge() {
bool ok = false;
st.clear();
st.insert(0);
ll sum = 0;
for(int i = n; i >= 1; i--) {
if((n-i) & 1) {
sum += a[i];
if(st.count(sum - k))
return true;
}else {
sum -= a[i];
if(st.count(sum + k))
return true;
}
st.insert(sum);
}
return false;
}
int main() {
int T, cas = 1;
scanf("%d", &T);
while(T--) {
scanf("%d%lld", &n, &k);
for(int i = 1; i <= n; i++) {
read(a[i]);
}
printf("Case #%d: ", cas++);
if(judge()) printf("Yes.\n"); else printf("No.\n");
}
return 0;
}