解いた問題のソースコードと解説など。


SRM 325 Div1 Medium ModularInequality

問題

数列Aと整数Pが与えられる。\sum_{i}|A_i - X|\le Pを満たす整数Xの個数を求めよ。

やりかた

数列の中央値に近い部分をXとすると題意の和は最も小さくなり、中央値から離れると和は大きくなっていく。式を満たすXは中央値付近でひとつづきに現れるので、これらの内の最大値と最小値が求まれば、この間の整数の個数が答えになる。
これらは両方共二分探索で求めることができる。

class ModularInequality {
public:
  vector<int> AA;
  ll labs(ll a){
    if(a > 0) return a;
    else return -a;
  }
  ll diff(ll a){
    ll sum = 0LL;
    for(int i = 0; i < (int)AA.size(); i++) sum += labs(AA[i] - a);
    return sum;
  }
  int countSolutions(vector <int> A, int P){
    sort(ALL(A));
    int N = A.size();
    AA = A;

    ll median = A[A.size() / 2];

    //式を満たす最小のXを求める
    ll ub = median + 1, lb = -LLINF;
    while(ub - lb > 1LL){
      ll mid = (ub + lb) / 2;
      if(diff(mid) > P) lb = mid;
      else ub = mid;
    }    
    ll left;
    for(left = ub - 100; left <= ub + 100; left++) //厳密な値を求める。とりあえず。。。
      if(diff(left) <= P) break;

    //式を満たす最大のXを求める
    ub = LLINF, lb = median - 1;
    while(ub - lb > 1LL){
      ll mid = (ub + lb) / 2;
      if(diff(mid) > P) ub = mid;
      else lb = mid;
    }
    ll right;
    for(right = ub + 100; right >= ub - 100; right--)
      if(diff(right) <= P) break;

    cout << left << " " << right << endl;
    return right < left ? 0 : right - left + 1;
  }
}
Get up! 明日のSUPER ST@R!