2 条题解

  • 0
    @ 2025-10-8 16:58:00

    示例:3 4 1 5 2 所有满足条件的序列: 2 3 4 5 1 3 4 5 1 2 4 5 1 2 3 5 1 2 3 4 分别求 3 4 1 5 2 经过多少次交换可以变成以上 4 种序列,选其中交换次数最少到。 要求 3 4 1 5 2 经过多少次交换可以变成 2 3 4 5 1,可以将 1 改为 6,然后求逆序对数量。 同理求:3 4 1 5 2 经过多少次交换可以变成 3 4 5 1 2,只需要将 1 改为 6,2改为 7 求逆序对。 考虑以下两个序列中的逆序对数的区别: 3 4 1 5 2 3 4 6 5 2 a. 1 前面的所有数都和 1 形成逆序对。 b. 6 后面的所有数都和 6 形成逆序对。 设值 i 所在位置为 p[i],则:减少了 p[i]-1 个逆序对,增加了 n-p[i]个逆序对。 设当前序列逆序对数量为 cnt,当前最小值为 i,则将 i 修改为最大值后逆序对数量变为: cnt - (p[i] - 1) + (n - p[i]); 只需要做一次求逆序对即可,总时间复杂度降为 O(Nlog(N) + N)

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    int n, a[N], pos[N]; ll c[N];
    
    void add(int x) { for (; x <= n; x += x & -x) ++c[x]; }
    ll sum(int x) { ll res = 0; for (; x; x -= x & -x) res += c[x]; return res; }
    
    int main() {
        scanf("%d", &n);
        memset(c, 0, sizeof(c));
        ll ans = 0;
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            add(a[i]);
            ans += i - sum(a[i]);
            pos[a[i]] = i;
        }
        ll t = ans;
        for (int i = 1; i <= n; ++i) { // 转移
            t = t - (pos[i] - 1) + (n - pos[i]);
            // 即将把 i 看成 i+n,那么i之前有 pos[i]-1 个 i 大的,i 后面有 n-pos[i]个比 i 小的
            ans = min(ans, t);
        }
        printf("%lld\n", ans);
        return 0;
    }
    
    • 0
      @ 2025-10-8 16:57:52
      /*
      示例:3 4 1 5 2 
      所有满足条件的序列:
      	2 3 4 5 1
      	3 4 5 1 2
      	4 5 1 2 3
      	5 1 2 3 4
      分别求 3 4 1 5 2 经过多少次交换可以变成以上 4 种序列,选其中交换次数最少到。
      要求 3 4 1 5 2 经过多少次交换可以变成 2 3 4 5 1,可以将 1 改为 6,然后求逆序对数量。
      同理求:3 4 1 5 2 经过多少次交换可以变成 3 4 5 1 2,只需要将 1 改为 6,2改为 7 求逆序对。
      考虑以下两个序列中的逆序对数的区别:
      	3 4 1 5 2
      	3 4 6 5 2
      	a. 1 前面的所有数都和 1 形成逆序对。
      	b. 6 后面的所有数都和 6 形成逆序对。
      设值 i 所在位置为 p[i],则:减少了 p[i]-1 个逆序对,增加了 n-p[i]个逆序对。
      设当前序列逆序对数量为 cnt,当前最小值为 i,则将 i 修改为最大值后逆序对数量变为:
      cnt - (p[i] - 1) + (n - p[i]); 
      只需要做一次求逆序对即可,总时间复杂度降为 O(Nlog(N) + N)
      */
      #include<bits/stdc++.h>
      using namespace std;
      typedef long long ll;
      const int N=1e5+5;
      int n,a[N],pos[N]; ll c[N];
      
      void add(int x){for(;x<=n;x+=x&-x)++c[x];}
      ll sum(int x){ll res=0;for(;x;x-=x&-x)res+=c[x];return res;}
      int main()
      {
          scanf("%d",&n);
          memset(c,0,sizeof(c));
          ll ans=0;
          for(int i=1;i<=n;++i)
          {
              scanf("%d",&a[i]);
              add(a[i]);
              ans+=i-sum(a[i]);
              pos[a[i]]=i;
          }
          ll t=ans;
          for(int i=1;i<=n;++i)//转移
          {
              t=t-(pos[i]-1)+(n-pos[i]);
              //即将把 i 看成 i+n,那么i之前有 pos[i]-1 个 i 大的,i 后面有 n-pos[i]个比 i 小的
              ans=min(ans,t);
          }
          printf("%lld\n",ans);
          return 0;
      }
      
      • 1

      *【树状数组:逆序对】循环同构的最少交换次数[USACO10NOV] Cow Photographs G(好题)

      信息

      ID
      1581
      时间
      1000ms
      内存
      128MiB
      难度
      10
      标签
      递交数
      1
      已通过
      1
      上传者