题目链接: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);
}
}