B . E l e m e n t S w a p p i n g B.\ Element\ Swapping B. Element Swapping
题意
有 n n n个数字现在交换了两个数字,给你交换前的序列的相关信息为 x = ∑ i = 1 n i ∗ a i , y = ∑ i = 1 n i ∗ a i ∗ a i x=\sum _{i=1}^{n}i*a_i,y =\sum^n_{i=1}i*a_i*a_i x=∑i=1ni∗ai,y=∑i=1ni∗ai∗ai。现在请问你最多有多少种交换方式?
题解
这题主要是一个数学的推导题,具体的思路可能不难,主要是一些小细节可能卡掉你的 A C AC AC。
我们令
x
1
,
y
1
x1,y1
x1,y1为当前序列的
x
,
y
x,y
x,y值,得到以下推理过程
{
x
−
x
1
=
(
k
1
−
k
2
)
∗
(
a
k
1
−
a
k
2
)
y
−
y
1
=
(
k
1
−
k
2
)
∗
(
a
k
1
−
a
k
2
)
∗
(
a
k
1
+
a
k
2
)
\left\{ \begin{array}{lr} x-x1=(k_1-k_2)*(a_{k_1}-a_{k2}) & \\ y-y1=(k_1-k_2)*(a_{k_1}-a_{k2})*(a_{k_1}+a_{k_2}) \end{array} \right.
{x−x1=(k1−k2)∗(ak1−ak2)y−y1=(k1−k2)∗(ak1−ak2)∗(ak1+ak2)
我们将两个结果除一下就能得到
(
y
−
y
1
)
(
x
−
x
1
)
=
a
k
1
+
a
k
2
\frac{(y-y1)}{(x-x1)}=a_{k_1}+a_{k_2}
(x−x1)(y−y1)=ak1+ak2
然后我们再通过第一个公式推出
k
1
−
k
2
=
x
−
x
1
a
k
1
−
a
k
2
k_1-k2=\frac{x-x1}{a_{k_1}-a_{k_2}}
k1−k2=ak1−ak2x−x1
剩下的就很明显了,我们可以
O
(
n
)
O(n)
O(n)枚举所有的元素判断其对应位置上的数是否符合我们上面给出的答案。
但存在以下几种特殊情况
-
x − x 1 = = 0 & & y − y 1 = = 0 x-x1==0\ \&\&\ y-y1==0 x−x1==0 && y−y1==0
说明交换的两个数字相同,那我们只要枚举相同的数字有多少个就行了。
-
x − x 1 ! = 0 & & y − y 1 = = 0 ∣ ∣ x − x 1 = = 0 & & y − y 1 ! = 0 x-x1!=0\ \&\&\ y-y1==0\ ||\ x-x1==0\ \&\&\ y-y1!=0 x−x1!=0 && y−y1==0 ∣∣ x−x1==0 && y−y1!=0
不合法,直接输出 0 0 0
-
( y − y 1 ) % ( x − x 1 ) ! = 0 (y-y1)\%(x-x1)\ !=\ 0 (y−y1)%(x−x1) != 0
不合法直接输出 0 0 0
最后需要注意的是当你枚举所有元素的时候可能会有重复计算的问题,我的选择是如果找到的元素的位置在当前位置之后直接跳过。
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve(){
int n;
i64 x1, y1, x2 = 0, y2 = 0;
cin >> n >> x1 >> y1;
vector<int> nums(n + 10);
for(int i = 1;i <= n;i ++){
cin >> nums[i];
x2 += i * nums[i];
y2 += i * nums[i] * nums[i];
}
i64 x = x1 - x2, y = y1 - y2;
i64 ans = 0;
map<int, i64> cnt;
if(x == 0 && y == 0){
for(int i = 1;i <= n;i ++) cnt[nums[i]] ++;
for(auto v : cnt) ans += v.second * (v.second - 1) / 2;
cout << ans << endl;
return ;
}else if(x == 0 || y == 0){
cout << 0 << endl;
return ;
}
i64 tmp = y / x;
for(int i = 1;i <= n;i ++){
int a1 = nums[i];
int a2 = tmp - a1;
if(a1 == a2) ans += cnt[a1] * (cnt[a1] - 1) / 2;
else{
int k2 = i + x / (a1 - a2);
if(!(1 <= k2 && k2 <= n) || x % (a1 - a2) != 0 || i == k2) continue;
if(nums[k2] == a2) ans ++;
}
}
cout << ans / 2 << endl;
return ;
}
int main(){
#ifdef DEBUG//数据量小 重定向
freopen("/Users/chenzhiyuan/Desktop/ACM/ACM/in.txt", "r", stdin);
freopen("/Users/chenzhiyuan/Desktop/ACM/ACM/out.txt", "w", stdout);
#endif
ios_base::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int _;
cin >> _;
while(_ --) solve();
return 0;
}
E . S e q u e n c e i n t h e P o c k e t E.\ Sequence\ in\ the\ Pocket E. Sequence in the Pocket
题意
现在有 n n n个数字,你可以将任意位置上的元素放到第一个,请问最少进行几次可以使得这个位置非降序。
题解
从后向前找最大的数字,找到最大的数字后再找次大的数字如此循环往复,输出还有几个数字没有找到即可。
因为要非降序,所以我们很明确最大的数字一定是放在最后面的,所有只要找到几个数字的相对位置不变,即可输出。
#include<bits/stdc++.h>
using namespace std;
int main(){
#ifdef DEBUG//数据量小 重定向
freopen("/Users/chenzhiyuan/Desktop/ACM/ACM/in.txt", "r", stdin);
#endif
ios_base::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int _;
cin >> _;
while(_--){
int n;
cin >> n;
vector<int> nums1(n + 10);
for(int i = 1;i <= n;i ++) cin >> nums1[i];
vector<int> nums2(nums1);
sort(nums1.begin() + 1,nums1.begin() + n + 1);
int ans = n;
for(int i = n;i >= 1;i --) if(nums2[i] == nums1[ans]) ans --;
cout << ans << endl;
}
return 0;
}