0w1

IOI 2016 Messy ( Interactive, Divide and Conquer )

http://ioinformatics.org/locations/ioi16/contest/day2/messy/messy-TWN.pdf
1960 - [IOI 2016] Messy | TIOJ INFOR Online Judge

題意:
你可以將任意數量的無號 n 位數丟 ( add_element() )進一個 set 裡面,然後呼叫 compile_set(),這個 set 裡面的所有數字都會依據一個未知的排列 p,以二進制表示,假設原本有個元素是 x*2^0+x*2^1+...+x*2^(n-1),那麼該數會變成 x*2^p[0]+x*2^p[1]+...+x*2^p[n-1]。你接下來可以問 ( check_element() ) 任意的數是否存在在這個 set 裡。最後要回傳 p 陣列。

資料規模:
n ≤ 128,保證 n 是 2 的冪次
add_element() 和 check_element() 分別呼叫不超過 896 次。

解法:
保證 n 是 2 的冪次又長得很構造,一臉分治的。
排列本身不好求,轉個方向考慮「這個下標是誰要的」,formally 就是 P[] 的反函數。
對於一個下標 i,如果知道 i 是 [ lb, mid ) 其中一個人要的,那麼 i 一定不是 [ mid, rb ) 要的,反之亦然。
模糊地考慮,往 set 丟進 [ lb, mid ) 每個位置分別有 1 個 1,一共有 mid - lb 個元素。那麼,compile_set() 之後如果對於任意在 [ lb, rb ) 的 i 有元素是位置 i 為 1,那麼 P[ i ] 一定介於 [ lb, mid )。這樣掃過去後,就能分離好,使得左半邊的解和右半邊的解無關。持續遞迴做便能得解。 最後,為了區隔每層,沒有用到的部分都是設為 1。

#include "lib1960.h"
#include <bits/stdc++.h>
using namespace std;

void rec_add( string &s, int lb, int rb ){
  if( lb + 1 == rb ) return;
  int mid = lb + rb >> 1;
  for( int i = lb; i < mid; ++i ){
    s[ i ] = '1';
    add_element( s.c_str() );
    s[ i ] = '0';
  }
  {
    for( int i = mid; i < rb; ++i ){
      s[ i ] = '1';
    }
    rec_add( s, lb, mid );
    for( int i = mid; i < rb; ++i ){
      s[ i ] = '0';
    }
  }
  {
    for( int i = lb; i < mid; ++i ){
      s[ i ] = '1';
    }
    rec_add( s, mid, rb );
    for( int i = lb; i < mid; ++i ){
      s[ i ] = '0';
    }
  }
}

void rec_solve( string &s, vector< int > &pos, int lb, int rb ){
  if( lb + 1 == rb ) return;
  int mid = lb + rb >> 1;
  for( int i = lb, ptr = lb; i < rb; ++i ){
    s[ pos[ i ] ] = '1';
    bool chk = check_element( s.c_str() ); // debugged QQ
    s[ pos[ i ] ] = '0';
    if( chk ){
      swap( pos[ i ], p[ ptr++ ] );
    }
  }
  {
    for( int i = mid; i < rb; ++i ){
      s[ pos[ i ] ] = '1';
    }
    rec_solve( s, p, lb, mid );
    for( int i = mid; i < rb; ++i ){
      s[ pos[ i ] ] = '0';
    }
  }
  {
    for( int i = lb; i < mid; ++i ){
      s[ pos[ i ] ] = '1';
    }
    rec_solve( s, p, mid, rb );
    for( int i = mid; i < rb; ++i ){
      s[ pos[ i ] ] = '0';
    }
  }
}

void restore_permutation( int n, int w, int r, int* result ){
  {
    string s;
    for( int i = 0; i < n; ++i ){
       s += "0";
    }
    rec_add( s, 0, n );
    compile_set();
  }
  {
    string s;
    for( int i = 0; i < n; ++i ){
       s += "0";
    }
    vector< int > pos( n );
    for( int i = 0; i < n; ++i ){
      pos[ i ] = i;
    }
    rec_solve( s, pos, 0, n );
    for( int i = 0; i < n; ++i ){
      result[ pos[ i ] ] = i;
    }
  }
}