• Codeforces 1343D



    题面

    Prob




    题意

    给定一个长度为 n 的数列,n 为偶数,保证每个元素在 [ 1 , k ] 之间

    每次操作可以把某个位置的数字变成 [ 1 , k ] 内的任意数字

    要求让这个数列满足:对于所有的 i ∈ [ 1 , n/2 ],a[i] + a[n-i+1] 是一个定值

    问最少的操作次数




    解题思路

    差分数组维护取某个值为定值时所需要的最少操作次数

    令差分数组为 delta

    每次取一组 a[i] 和 a[n-i+1] 处理

    sum = a[i]+a[n-i+1] , maxn=max(a[i],a[n-i+1]) , minn=min(a[i],a[n-i+1])


    分类讨论得到:

      一、如果定值 x 在 [2,minn] 之间,即使将较大的数更改为1后,和也是大于x,说明此时这两个数都需要更改,所以这段区间的操作数+2

      二、如果定值 x 在 [maxn+k+1,2*k] 之间,即使将较小的数更改为k后,和也是小于x,说明此时这两个数都需要更改,所以这段区间的操作数+2

      三、如果定值 x 在 [minn+1,maxn+k] 之间且不等于 sum,能够做到只改变其中一个数就使得和等于x,所以这个范围内操作数+1

      四、特殊处理,如果定值 x 等于 sum,不需要更改任何一个数,所以这个点的操作数不需要增加


    综上,对于差分数组的标记,我们可以得到:

    delta[2]+=2;
    delta[minn+1]-=2;
    //讨论 1
    delta[maxn+k+1]+=2;
    delta[2*k+1]-=2;
    //讨论 2
    delta[minn+1]++;
    delta[maxn+k+1]--;
    delta[sum]--;
    delta[sum+1]++;//因为上面两步把sum位置加上了1,所以这里减去1
    //讨论 3、4
    

    差分数组的标记原理为:如果要让区间 [l,r] 内每个元素加上 x ,则 delta[l]+=x , delta[r+1]-=x ,最后遍历从前往后依次加上前一个数作为答案


    对于代码中“讨论 3、4”为什么sum先加后减的解释

    如果要求的和恰好是当前处理的两数之和 sum

    那么 sum 这个点的操作次数不需要增加

    又因为 sum∈[ minn+1 , maxn+k ]

    所以理解方法可以是以下这两种——

    • 先在整段区间上 +1 ,再将sum这个点单独 -1

      整段区间 +1 ,即delta[minn+1]++; delta[maxn+k+1]--;

      单独将 sum 这个点 -1 ,即加上负 1 ,即delta[sum]--; delta[sum+1]++;

    • 将区间看成两段,分别是 [ minn+1 , sum-1 ] 和 [ sum+1 , maxn+k ] ,然后两段分别 +1

      前一段即delta[minn+1]++; delta[sum]--;

      后一段即delta[sum+1]++; delta[maxn+k+1]--;

    两种理解的代码合起来是相同的



    对于 2*k+1 这个位置,是差分数组标记的结尾,可以选择不进行处理

    所以整理得到:

    delta[2]+=2;
    delta[minn+1]--;
    delta[maxn+k+1]++;
    delta[sum]--;
    delta[sum+1]++;
    

    最后从 2 位置开始到 2*k 处理一遍差分数组即可

    ans=delta[2];
    for(int i=3;i<=2*k;i++)
    {
        delta[i]+=delta[i-1];
        ans=min(delta[i],ans);
    }
    



    完整程序

    (62ms/1000ms)

    在 i ∈ [1,n/2] 时,n-i+1会返回数列的后半部分与 i 对应的位置

    在 i ∈ [n/2+1,n] 时,n-i+1会返回数列的前半部分与 i 对应的位置

    所以在 i>n/2 时使用 ar[i] 与 ar[n-i+1] 也是对的

    #include<bits/stdc++.h>
    using namespace std;
    
    int ar[200050],delta[400050]; //差分数组开2*k的空间
    
    void solve()
    {
        int n,k,sum,minn,maxn,ans;
    
        cin>>n>>k;
        memset(delta,0,(2*k+10)*sizeof(int)); //初始化到2*k即可(稍大)
    
        for(int i=1;i<=n;i++)
        {
            cin>>ar[i];
            if(i>n/2)
            {
                sum=ar[i]+ar[n-i+1];
                minn=min(ar[i],ar[n-i+1]);
                maxn=sum-minn;
    
                delta[2]+=2;
                delta[minn+1]--;
                delta[maxn+k+1]++;
                delta[sum]--;
                delta[sum+1]++;
            }
        }
    
        ans=delta[2];
        for(int i=3;i<=2*k;i++)
        {
            delta[i]+=delta[i-1];
            ans=min(delta[i],ans);
        }
    
        cout<<ans<<'
    ';
    }
    
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        for(int t=1;t<=T;t++)
            solve();
        return 0;
    }
    

  • 相关阅读:
    网站无脑搭建,自己可以建个站玩一玩儿
    Python2和Python3中urllib库中urlencode的使用注意事项
    图像的缩放与图像金字塔
    模糊聚类算法(FCM)
    java调用python脚本
    雷林鹏分享:jsp HTTP 状态码
    雷林鹏分享:jsp 服务器响应
    雷林鹏分享:jsp 客户端请求
    雷林鹏分享:jsp 隐式对象
    雷林鹏分享:jsp 动作元素
  • 原文地址:https://www.cnblogs.com/stelayuri/p/12749332.html
Copyright © 2020-2023  润新知