题目链接:B - Element Swapping
题意
给你一个长度为n的序列,可能会从序列里选两个数进行交换,交换次数只能为一次,现在我们知道原序列的 x = ∑ k = 1 n k ∗ a k 和 y = ∑ k = 1 n k ∗ a k 2 {x=\sum_{k=1}^n{k*a_k}和y=\sum_{k=1}^n{k*a_k^2}} x=∑k=1nk∗ak和y=∑k=1nk∗ak2,以及交换后新序列的每个值,问可能交换的次数。
题解
假设新序列为
a
1
,
a
2
,
a
3
.
.
a
i
,
a
i
+
1
,
.
.
.
a
j
,
a
j
+
1
.
.
.
a
n
{a_1,a_2,a_3..a_i,a_{i+1},...a_j,a_{j+1}...a_n}
a1,a2,a3..ai,ai+1,...aj,aj+1...an
交换的数为
a
i
,
a
j
{a_i,a_j}
ai,aj
那么原数列为
a
1
,
a
2
,
a
3
.
.
a
j
,
a
i
+
1
,
.
.
.
a
i
,
a
j
+
1
.
.
.
a
n
{a_1,a_2,a_3..a_j,a_{i+1},...a_i,a_{j+1}...a_n}
a1,a2,a3..aj,ai+1,...ai,aj+1...an
我们可以列一个方程组
{ T 1 = ∑ k = 1 n k ∗ a k x = ∑ k = 1 n k ∗ a k ( k ! = i & & k ! = j ) + i ∗ a j + j ∗ a i T 2 = ∑ k = 1 n k ∗ a k 2 y = ∑ k = 1 n k ∗ a k 2 ( k ! = i & & k ! = j ) + i ∗ a j 2 + j ∗ a i 2 { \begin{cases} T_1=\sum_{k=1}^n{k*a_k} \\ x=\sum_{k=1}^n{k*a_k} (k!=i \&\& k!=j) +i*a_j+j*a_i\\ T_2=\sum_{k=1}^n{k*a_k^2} \\ y=\sum_{k=1}^n{k*a_k^2} (k!=i \&\& k!=j) +i*a_j^2+j*a_i^2\\ \end{cases}} ⎩⎪⎪⎪⎨⎪⎪⎪⎧T1=∑k=1nk∗akx=∑k=1nk∗ak(k!=i&&k!=j)+i∗aj+j∗aiT2=∑k=1nk∗ak2y=∑k=1nk∗ak2(k!=i&&k!=j)+i∗aj2+j∗ai2
可以通过等式相加减得
{ T 1 − x = ( a i − a j ) ∗ ( i − j ) T 2 − y = ( a i 2 − a j 2 ) ∗ ( i − j ) { \begin{cases} T_1-x=(a_i-a_j)*(i-j) \\ T_2-y=(a_i^2-a_j^2)*(i-j) \\ \end{cases}} {T1−x=(ai−aj)∗(i−j)T2−y=(ai2−aj2)∗(i−j)
我们发现下面的等式比上面的多乘了一个 ( a i + a j ) {(a_i+a_j)} (ai+aj)
注意这里的 T 1 和 T 2 {T_1和T_2} T1和T2是交换后新序列的值,但有可能题目中给的序列根本无法完成交换,所以我们首先需要判断给的新序列的 T 1 和 T 2 {T_1和T_2} T1和T2是否符合条件。
1.如果
(
T
1
=
x
&
&
T
2
=
y
)
{(T_1=x \&\& T_2=y)}
(T1=x&&T2=y),说明新序列和原序列相等,那么对于序列里面相同的数字可以互相交换,我们只需统计序列里相同的数字有多少个,然后排列组合就行。
2.如果
(
T
1
=
x
)
{(T_1=x)}
(T1=x),分母为0,不符合情况,答案为0。
3.如果
(
T
2
−
y
)
%
(
T
1
−
x
)
≠
0
{(T_2-y)\%(T_1-x)≠0}
(T2−y)%(T1−x)=0,说明
(
a
i
+
a
j
)
{(a_i+a_j)}
(ai+aj)不为整数,不合题意,答案为0。
4.如果
(
T
2
−
y
)
/
(
T
1
−
x
)
≤
0
{(T_2-y)/(T_1-x)≤0}
(T2−y)/(T1−x)≤0,说明
(
a
i
+
a
j
)
≤
0
{(a_i+a_j)≤0}
(ai+aj)≤0,不合题意,答案为0。
基本上判断完这些,我们已经可以确定所给的 T 1 和 T 2 {T_1和T_2} T1和T2是符合条件的,剩下的就是去寻找符合交换条件的有多少种情况。
由上面可知
{
a
i
+
a
j
=
(
T
2
−
y
)
/
(
T
1
−
x
)
x
−
T
1
=
(
a
i
−
a
j
)
∗
(
j
−
i
)
{ \begin{cases} a_i+a_j=(T_2-y)/(T_1-x) \\ x-T_1=(a_i-a_j)*(j-i) \\ \end{cases}}
{ai+aj=(T2−y)/(T1−x)x−T1=(ai−aj)∗(j−i)
我们只需寻找有多少个i和j符合上述条件即可。
暴力枚举很明显是不明智的。
我们可以枚举i,那么
a
i
、
i
{a_i、i}
ai、i以及
a
j
{a_j}
aj都清楚了,那么
j
=
(
x
−
T
1
)
/
(
a
i
−
a
j
)
+
i
{j=(x-T_1)/(a_i-a_j)+i}
j=(x−T1)/(ai−aj)+i。
我们只需判断
j
是
否
等
于
a
j
即
可
{j是否等于a_j即可}
j是否等于aj即可。
注意交换是相互的所以算出的
(
(
x
−
T
1
)
/
(
a
i
−
a
j
)
+
i
)
{((x-T_1)/(a_i-a_j)+i)}
((x−T1)/(ai−aj)+i)一定得大于i才行。
代码
#include<iostream>
#include<sstream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cmath>
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
typedef long double ld;
#define pll pair<ll,ll>
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
const int pi = acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int maxn=1e5+10;
int a[maxn];
ll book[maxn];
int main()
{
int t; scanf("%d",&t);
while(t--)
{
memset(book, 0, sizeof(book));
ll n,x,y; scanf("%lld%lld%lld",&n,&x,&y);
ll t1=0,t2=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
t1+=(ll)i*a[i]; t2+=(ll)i*a[i]*a[i];
book[a[i]]++;
}
ll ans=0;
if(t1-x==0 && t2-y==0)
{
for(int i=1;i<=100000;i++)
ans+=(book[i]-1)*book[i]/2;
printf("%lld\n",ans);
continue;
}
if(t1-x==0)
{
printf("0\n");
continue;
}
if((t2-y)%(t1-x))
{
printf("0\n");
continue;
}
long long num=(t2-y)/(t1-x);
if(num<=0)
{
printf("0\n");
continue ;
}
for(int i=1;i<=n;i++)
{
int aj=num-a[i],s=a[i]-aj;
if(s!=0 && aj>0)
{
int cnt=(x-t1)/s;
cnt+=i;
if(cnt>=1 && cnt<=n && cnt>i && a[cnt]==aj) ans++;
}
}
printf("%lld\n",ans);
}
}