题目大意:
有一个数组,被一个人交换了两个值,而原主人又忘了数组的样子,但是原主人记得两个值:
以及现在数组,问你有几种交换的可能?(可能交换了哪两个数字?)
设现在的数组的 Xi = i * ai + j * aj + “…” ,Yi = i * ai ^ 2 + j * aj ^ 2 + “…”
原数组的 X = i * aj + j * ai + “…”, Y = i * aj ^ 2 + j * ai ^ 2 + “…”
ai,aj是交换的部分,反过来我们从现数组变回原数组来思考也是一样的,对于其他没交换的部分我们不关心,当成黑盒处理。
上下两个式子相减:得到 Xi - X = (j - i) * (aj - ai), Yi - Y = (ai + aj) * (Xi - X)
ai + aj = (Yi - Y) / (Xi - X)。因为题目的输入是随机的,各种情况都可能发生(以及输入的X,Y甚至不是原数组的求出来的值),这里要特判 Xi - X为0 的情况,以及 (Yi - Y) % (Xi - X) != 0的情况。
对于Xi - X 为 0的情况,若Yi - Y 不为0,则答案一定为0,若Yi - Y为0,则交换任意两个数值相等的数字都是可以的,统计所有可能的答案。
若都不为0, (Yi - Y) % (Xi - X) != 0 说明ai + aj 不是整数,违背题意,一定是无解,输出0。
否则:
求出ai + aj,我们可以枚举ai来求得aj,然后根据 Xi - X = (j - i) * (aj - ai) ,(交换两个数字导致X的改变量),可以求出 j 的位置,这个时候只要 现在数组的a[j] = aj ,说明这i,j就是一个可能的答案,对答案+1
也存在aj - ai = 0 的情况,和(Xi - X) % ( aj - ai) != 0的情况,直接跳过(这里已经是 Xi - X 不为0的情况,这些都是不合法的情况)。
(上面是一个很好的合并答案方式,采用验证的方法,在原数组中验证j位置数值是否是aj,而不是处理所有值的位置来统计答案,我原来的写法是开 maxn 个桶存数组每个数值的位置,枚举每个数值ai,算出aj,以及它们的位置的差值(j - i),然后遍历ai的所有位置,遍历aj 的所有位置,位置差值符合要求的时候 答案+1,统计比较复杂,可以用two pointer 技巧使得合并答案不会T掉,但我哇了,换成了这种更具技巧性的方式)。
注意只枚举j > i 的情况就可以覆盖所有答案,j < i的情况舍弃(想起了高中班主任的一句话,想得越多,写得越少,凡事还是要多思考)
这题需要思考的细节比较多,需要严谨的思维,很值得补。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
long long t,n,m;
long long x,y;
long long a[maxn],vis[maxn];
vector<int> g[maxn];
long long tt[maxn],tot,mx,pos;
int main() {
scanf("%lld",&t);
while(t--) {
scanf("%lld",&n);
scanf("%lld%lld",&x,&y);
long long xi = 0,yi = 0;
for(int i = 1; i <= n; i++) {
scanf("%lld",&a[i]);
vis[a[i]]++;
}
for(long long i = 1; i <= n; i++) {
xi += a[i] * i;
yi += a[i] * a[i] * i;
}
long long t1 = yi - y;
long long t2 = xi - x; // 现在的x与原x的差值
if(t2 == 0) {
if(t1 != 0) {
printf("0\n");
}
else {
long long ct = 0;
for(int i = 1; i < maxn; i++) {
ct += vis[i] * (vis[i] - 1) / 2;
}
printf("%lld\n",ct);
}
}
else if(abs(t1) % abs(t2) != 0) {
printf("0\n");
}
else {
long long tmp = t1 / t2; // ai + aj 的值
if(tmp > 1000000 || t2 * tmp + y != yi) {
printf("0\n");
}
else {
long long res = 0;
for(long long i = 1; i <= n; i++) {
long long vj = tmp - a[i];
long long d = vj - a[i];
if(d == 0 || abs(t2) % abs(d)) continue;
long long j = t2 / d + i;
if(j <= i) continue;
if(a[j] == vj) res++;
}
printf("%lld\n",res);
}
}
for(int i = 1; i <= n; i++) {
vis[a[i]] = 0;
}
}
return 0;
}