[2019年浙江省省赛] B - Element Swapping 数学推导

题目链接: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=1nkaky=k=1nkak2,以及交换后新序列的每个值,问可能交换的次数。

题解

假设新序列为 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} aiaj
那么原数列为 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=1nkakx=k=1nkak(k!=i&&k!=j)+iaj+jaiT2=k=1nkak2y=k=1nkak2(k!=i&&k!=j)+iaj2+jai2

可以通过等式相加减得

{ 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}} { T1x=(aiaj)(ij)T2y=(ai2aj2)(ij)

我们发现下面的等式比上面的多乘了一个 ( a i + a j ) {(a_i+a_j)} (ai+aj)

注意这里的 T 1 和 T 2 {T_1和T_2} T1T2是交换后新序列的值,但有可能题目中给的序列根本无法完成交换,所以我们首先需要判断给的新序列的 T 1 和 T 2 {T_1和T_2} T1T2是否符合条件。

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} (T2y)%(T1x)=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} (T2y)/(T1x)0,说明 ( a i + a j ) ≤ 0 {(a_i+a_j)≤0} (ai+aj)0,不合题意,答案为0。

基本上判断完这些,我们已经可以确定所给的 T 1 和 T 2 {T_1和T_2} T1T2是符合条件的,剩下的就是去寻找符合交换条件的有多少种情况。

由上面可知
{ 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=(T2y)/(T1x)xT1=(aiaj)(ji)
我们只需寻找有多少个i和j符合上述条件即可。
暴力枚举很明显是不明智的。

我们可以枚举i,那么 a i 、 i {a_i、i} aii以及 a j {a_j} aj都清楚了,那么 j = ( x − T 1 ) / ( a i − a j ) + i {j=(x-T_1)/(a_i-a_j)+i} j=(xT1)/(aiaj)+i
我们只需判断 j 是 否 等 于 a j 即 可 {j是否等于a_j即可} jaj
注意交换是相互的所以算出的 ( ( x − T 1 ) / ( a i − a j ) + i ) {((x-T_1)/(a_i-a_j)+i)} ((xT1)/(aiaj)+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);
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_44235989/article/details/108053472