ZOJ:Element Swapping(数学推导)

题目大意:
有一个数组,被一个人交换了两个值,而原主人又忘了数组的样子,但是原主人记得两个值:在这里插入图片描述
以及现在数组,问你有几种交换的可能?(可能交换了哪两个数字?)
设现在的数组的 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;
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/89635649
ZOJ