1. 程式人生 > 其它 >nowcoder11166G Game of Swapping Numbers(2021牛客暑期多校訓練營1)貪心 抽屜原理

nowcoder11166G Game of Swapping Numbers(2021牛客暑期多校訓練營1)貪心 抽屜原理

題意

給定兩個等長陣列\(A,B\),任意交換\(A\)中的兩個元素\(K\)次,求\(max\{\sum_{i=1}^N\mid A_i-B_i\mid\}\)\(2\le N\le5×{10}^5,0\le K\le{10}^8,-{10}^8\le A_i,B_i\le{10}^8\)

分析

先考慮交換任意次的情況。求和時,我們考慮去掉絕對值,則相當於為每一對\(A_i,B_i\)分配了一個正號和一個負號。考慮重新分配正負號:由於一對\(A_i,B_i\)的正負號與真實情況相反一定不是更優,那麼對於\(A\)的一個新的排列,交換任意次的最大值就為\(A\)\(B\)兩個數組合並後最大的\(N\)

個減最小的\(N\)個。

再由鴿巢原理,在\(N>2\)時,\(A\)中一定有兩個正或兩個負。交換至最大值後,任意正號的數一定大於所有負號的數,對於多餘的次數只需交換兩個正號或者負號,那麼交換次就可以等價於交換至多\(K\)次。\(N=2\)的情況特判即可。

觀察重新分配正負號後的兩個陣列,對於\(A_i,B_i\)一正一負的不需要移動\(A_i\)就可以滿足最大值的狀態,對於兩個正號的需要\(A_i\)與兩個負號的\(A_j\)交換才能滿足最大值。對於一次交換,新增的貢獻為\((A_i+B_i)-(max(A_i,B_i)-min(A_i,B_i))=2*min(A_i,B_i)\)。同理,對於兩個負號新增的貢獻為\(-2*max(A_i,B_i)\)

。對於每次交換,貪心地取最大的兩個即可。

程式碼

#include <bits/stdc++.h>
using namespace std;
constexpr int N(5e5+5);
using pii=pair<int,int>;
int a[N],b[N],x[N];
pii c[N*2];

int main() {
  int n,k;
  cin>>n>>k;
  for(int i=0;i<n;i++) {
    cin>>a[i];
    c[i]={a[i],i};
  }
  for(int i=0;i<n;i++) {
    cin>>b[i];
    c[i+n]={b[i],i};
  }
  sort(c,c+n*2);
  for(int i=0;i<n;i++)
    x[c[i].second]--;
  for(int i=n;i<n*2;i++)
    x[c[i].second]++;
  priority_queue<pii>q[2];
  long long ans=0;
  for(int i=0;i<n;i++) {
    if(x[i]==2)
      q[0].push({2*min(a[i],b[i]),i});
    else if(x[i]==-2)
      q[1].push({-2*max(a[i],b[i]),i});
    ans+=abs(a[i]-b[i]);
  }
  int t=q[0].size();
  if(n==2) {
    if(k%2)
      ans=abs(a[0]-b[1])+abs(a[1]-b[0]);
    else
      ans=abs(a[0]-b[0])+abs(a[1]-b[1]);
  }
  else {
    t=min(k,t);
    while(t--) {
      ans+=q[0].top().first+q[1].top().first;
      q[0].pop();
      q[1].pop();
    }
  }
  cout<<ans<<'\n';

  return 0;
}