【数学+分类讨论】Element Swapping(ZOJ-4101)

链接

ZOJ-4101

题意

数 组 a 通 过 交 换 一 对 数 字 , 得 到 了 b 数 组 。 数组a通过交换一对数字,得到了b数组。 a,b
给 出 如 下 的 两 个 值 x = ∑ k = 1 n k a k 和 y = ∑ k = 1 n k a k 2 和 b 数 组 . 给出如下的两个值x = \sum_{k=1}^{n}ka_k和y = \sum_{k=1}^{n}ka^2_k和b数组. x=k=1nkaky=k=1nkak2b.
问 可 能 有 多 少 种 交 换 方 式 使 得 满 足 条 件 。 问可能有多少种交换方式使得满足条件。 使
x 和 y 的 值 有 可 能 是 和 数 组 不 匹 配 的 , 这 样 的 情 况 时 输 出 0 。 x和y的值有可能是和数组不匹配的,这样的情况时输出0。 xy0

题解

已 知 x = ∑ k = 1 n k a k 和 y = ∑ k = 1 n k a k 2 , 已知x = \sum_{k=1}^{n}ka_k和y = \sum_{k=1}^{n}ka^2_k, x=k=1nkaky=k=1nkak2
则 令 x ′ = ∑ k = 1 n k b k 和 y ′ = ∑ k = 1 n k b k 2 , 则令x' = \sum_{k=1}^{n}kb_k和y' = \sum_{k=1}^{n}kb^2_k, x=k=1nkbky=k=1nkbk2
假 设 a i , a j 交 换 成 为 了 b i , b j , 即 a i = b j , a j = b i 。 假设a_i,a_j交换成为了b_i,b_j,即a_i=b_j,a_j=b_i。 aiajbibjai=bjaj=bi

有 X = x ′ − x = i b i + j b j − i a i − j a j = ( b i − b j ) ∗ ( i − j ) 有X=x'-x=ib_i+jb_j-ia_i-ja_j=(b_i-b_j)*(i-j) X=xx=ibi+jbjiaijaj=(bibj)(ij)
有 Y = y ′ − y = i b i 2 + j b j 2 − i a i 2 − j a j 2 = ( b i 2 − b j 2 ) ∗ ( i − j ) 有Y=y'-y=ib_i^2+jb_j^2-ia_i^2-ja_j^2=(b_i^2-b_j^2)*(i-j) Y=yy=ibi2+jbj2iai2jaj2=(bi2bj2)(ij)

易 得 , Y X = ( b i 2 − b j 2 ) ∗ ( i − j ) ( b i − b j ) ∗ ( i − j ) = b i + b j = C ( 常 数 ) 易得,\frac{Y}{X}=\frac{(b_i^2-b_j^2)*(i-j)}{(b_i-b_j)*(i-j)}=b_i+b_j=C(常数) XY=(bibj)(ij)(bi2bj2)(ij)=bi+bj=C
我 们 知 道 了 两 个 交 换 的 数 之 和 , 则 可 通 过 b i 求 得 b j = Y X − b i , 枚 举 b i , 可 求 得 b j , 然 后 可 通 过 X = ( b i − b j ) ∗ ( i − j ) 得 到 j 的 下 标 i − X b i − b j , 只 需 验 证 求 得 的 下 标 j 是 否 满 足 b j 即 可 。 我们知道了两个交换的数之和,则可通过b_i求得b_j=\frac{Y}{X}-b_i,枚举b_i,可求得b_j,然后可通过X=(b_i-b_j)*(i-j)得到j的下标i-\frac{X}{b_i-b_j},只需验证求得的下标j是否满足b_j即可。 bibj=XYbibibj,X=(bibj)(ij)jibibjX,jbj

需 要 注 意 的 是 : 需要注意的是: :
当 X ≠ 0 , Y % X ≠ 0 时 , x , y 不 匹 配 , a n s = 0 ; 当X \not = 0,Y\%X \not =0时,x,y不匹配,ans=0; X=0Y%X=0,x,yans=0;
当 X = 0 , Y ≠ 0 时 , x , y 不 匹 配 , a n s = 0 ; 当X = 0,Y \not = 0时,x,y不匹配,ans=0; X=0Y=0,x,yans=0;
当 X = Y = 0 时 , 因 为 交 换 前 后 无 变 化 , 则 a i = a j , 枚 举 每 个 数 出 现 的 次 数 n u m i , a n s = ∑ i = 1 n n u m i ∗ ( n u m i − 1 ) 2 当X=Y=0时,因为交换前后无变化,则a_i=a_j,枚举每个数出现的次数num_i,ans=\sum_{i=1}^{n}\frac{num_i*(num_i-1)}{2} X=Y=0ai=aj,numians=i=1n2numi(numi1)

代码

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;

#define ll long long

ll T,n,x,y,c;
ll suma,sumb,sum;
ll a[200500],b[200500];
unordered_map<ll,ll>dyy;
int main(){
	cin>>T;
	while(T--)
	{
		cin>>n>>x>>y;
		suma=sumb=sum=0;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];suma+=a[i]*i;
			b[i]=a[i]*a[i];sumb+=b[i]*i;
		}
		x=suma-x;//X
		y=sumb-y;//Y
		if(x!=0)
		{
			if(y%x)
			{
				cout<<0<<endl;
				continue;
			}
			else c=y/x;//c=ai+aj
			for(int i=1;i<n;i++)
			{
				ll aj=c-a[i];
				if(a[i]-aj==0)continue;//检查除数a_i-a_j是否为0,因为X>0,为0则跳过
				if(x%(a[i]-aj))continue;//防止x和y不匹配
				int j=i-x/(a[i]-aj);//计算j的下标
				if(j<=i||j>n)continue;//防止重复,对于每个i只查找之后的j
				if(a[j]==aj)sum++;
			}
		}
		else
		{
			if(y!=0)
			{
				cout<<0<<endl;
				continue;
			}
			dyy.clear();
			for(int i=1;i<=n;i++)
			{
				dyy[a[i]]++;
			}
			for(auto t:dyy)
			sum+=t.second*(t.second-1)/2;
		}
		cout<<sum<<endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值