土下座しながら探索中

主に競技プログラミング

Codeforces 496D : Tennis Game

問題リンク : Problem - 496D - Codeforces

問題概要 :
2人でテニスをしたときの記録が与えられる
このテニスはsセット先取した人が勝利する
1セットはtポイント先取した人のものとなる
与えられた記録から考えられるsとtの組み合わせをすべて求めよ
記録の長さnは高々10^5

解法 :
1セットをとるために必要なポイント数tを全て試す
tが決まるとsも一意に定まる
tを決めた後は、player1とplayer2のどちらが先にt点をとるかをシュミレートしていく
愚直にやるとO(n^2)でTLEするので累積和と二分探索で高速化する
1回で記録をt進めるので、計算する回数はn + n/2 + n/3 + ...
これはO(nlogn)となるので結局, n * nlogn * logn となり間に合う

コード :

#include<bits/stdc++.h>

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)

using namespace std;

typedef pair<int,int> ii;
vector<vector<int> > sum;
vector<int> arr;
int n;

bool check(int coef,int sp,int t,int player){
  int total = sum[sp+coef][player] - sum[sp-1][player];
  return total >= t;
}

int binarySearch(int sp,int t,int player){
  int L = 0, R = n-sp;
  while( L+1 < R ){
    int M = ( L + R ) / 2;
    if( check(M,sp,t,player) ) R = M;
    else                       L = M;
  }
  if( check(L,sp,t,player) ) return L;
  if( check(R,sp,t,player) ) return R;
  return -1;
}

int main(){
  vector<ii> answer;
  scanf("%d",&n);
  arr.resize(n+1,0);
  sum.resize(n+1,vector<int>(2,0));
  vector<int> next(n+1,-1);
  REP(i,1,n+1) scanf("%d",&arr[i]);
  REP(i,1,n+1) {
    --arr[i];
    sum[i][0] = sum[i-1][0] + !arr[i];
    sum[i][1] = sum[i-1][1] +  arr[i];
  }
  int winner = arr[n];
  for(int t=1;t<=n;t++){
    int win[2] = {0,0};
    int sp = 1;
    bool valid = true;
    while( sp <= n ){
      int pos[2];
      pos[0] = binarySearch(sp,t,0);
      pos[1] = binarySearch(sp,t,1);
      if( pos[0] != -1 ) pos[0] = sp + pos[0];
      if( pos[1] != -1 ) pos[1] = sp + pos[1];
      if( pos[0] == -1 || pos[1] == -1 ) {
        if( pos[0] == -1 && pos[1] == -1 ) break;
        if( pos[0] == -1 ) { ++win[1]; sp = pos[1]+1; }
        else               { ++win[0]; sp = pos[0]+1; }
      } else {
        assert( pos[0] != pos[1] );
        if( pos[0] < pos[1] ) { ++win[0]; sp = pos[0]+1; }
        else                  { ++win[1]; sp = pos[1]+1; }
      }
    }
    if( sp != n+1 ) valid = false;
    if( win[winner] <= win[!winner] ) valid = false;
    if( valid ) answer.push_back(ii(win[winner],t));
  }
  printf("%d\n",(int)answer.size());
  sort(answer.begin(),answer.end());
  answer.erase(unique(answer.begin(),answer.end()),answer.end());
  rep(i,(int)answer.size()) printf("%d %d\n",answer[i].first,answer[i].second);
  return 0;
}