2019浙江省赛

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=1niai,y=i=1niaiai。现在请问你最多有多少种交换方式?

题解

这题主要是一个数学的推导题,具体的思路可能不难,主要是一些小细节可能卡掉你的 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. {xx1=(k1k2)(ak1ak2)yy1=(k1k2)(ak1ak2)(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} (xx1)(yy1)=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}} k1k2=ak1ak2xx1
剩下的就很明显了,我们可以 O ( n ) O(n) O(n)枚举所有的元素判断其对应位置上的数是否符合我们上面给出的答案。

但存在以下几种特殊情况

  • x − x 1 = = 0   & &   y − y 1 = = 0 x-x1==0\ \&\&\ y-y1==0 xx1==0 && yy1==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 xx1!=0 && yy1==0  xx1==0 && yy1!=0

    不合法,直接输出 0 0 0

  • ( y − y 1 ) % ( x − x 1 )   ! =   0 (y-y1)\%(x-x1)\ !=\ 0 (yy1)%(xx1) != 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值