ei1333の日記

ぺこい

Codeforces Round #355 (Div. 2)

ABCDを出してBDは落ちた. 悪い癖.
ちょっとやらかしてしまったよね...

A. Vanya and Fence

問題概要

高さ  {h (1 \le h \le 1000)} のフェンスがある.
 {n (1 \le n \le 1000)} の人がいて身長はそれぞれ  {a_{i} (1 \le a_{i} \le 2h)} であり, 幅は 1 である. 人はしゃがむことができて, しゃがむと身長は半分になるが幅が 2 になる.

フェンス沿いの道路にそって,  {n}人の人が1列になって歩く.  {h} を超えないようにするとき, 必要な道路の最小値を求めよ.

解法

明らかに  {h}を超える人々のみしゃがめばよい. その人数を  {x} とすると, 答えは  {n + x} となる.

ソース

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

int main()
{
  int N, H;
  cin >> N >> H;

  int ret = 0;
  while(N--) {
    int a;
    cin >> a;
    ++ret;
    if(a > H) ++ret;
  }
  cout << ret << endl;
}

B. Vanya and Food Processor

問題概要

芋粉砕機がある. 芋粉砕機には随時芋を何個か重ねて入れることが可能だが, 一度に入れることが可能な量は高々  {h (1 \le h \le 10^9)} である.
1回の操作で  {k} までの芋を潰すことができる.

大きさ  {a_i} n 個の芋を順番に入れる. 最小何回の操作ですべての芋を潰すことができるか.

解法

頑張って限界まで芋を入れながらシミュレーションする.
芋をオーバーキルしそうなときは, 次の芋を見てがんばる.

ソース

int 型でオーバーフローすることを失念していた...

#include <bits/stdc++.h>
using namespace std;
#define int long long

signed main()
{
  int N, H, K;
 
  cin >> N >> H >> K;

  queue < int > A;
  for(int i = 0; i < N; i++) {
    int a;
    cin >> a;
    A.push(a);
  }

  
  int stock = 0;
  int ret = 0;
  
  while(true) {
    while(!A.empty() && stock + A.front() <= H) {
      A.pop();
    }

    int which = stock / K;
    ret += which;
    stock -= which * K;
    
    if(A.empty()) break;
    
    if(stock + *A.front() > H) {
      ++ret;
      stock = 0;
    }
  }
  
  cout << ret + (stock != 0) << endl;
}

C. Vanya and Label

問題概要

64進数の文字列  {s (1 \le |s| \le 100 000)} が与えられる.
bit単位のANDをとった時,  {s} となるような 2 つの文字列のペアの候補数を  {10^9 + 7} で割った余りで求めよ.

解法

まず, ANDは文字位置で独立なので, それぞれの文字で分離して考えることができる.
すると, ANDをして文字  {c} になるような文字ペアを求めたくなるが、これは前計算で簡単にできる.
あとは掛けるだけ.

ソース

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

const int mod = 1e9 + 7;

string poyo = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";

int main()
{

  int lookup[64] = {};
  for(int i = 0; i < 64; i++) {
    for(int j = 0; j < 64; j++) {
      for(int k = 0; k < 64; k++) {
        if((j & k) == i) lookup[i]++;
      }
    }
  }
  
  string s;
  cin >> s;

  int ret = 1;
  for(int i = 0; i < s.size(); i++) {
    ret = (1LL * ret * lookup[poyo.find(s[i])]) % mod;
  }
  cout << ret << endl;
}

D. Vanya and Treasure

問題概要

 {n \times m (1 \le n, m \le 300)} の 2 次元グリッドのマップがある.
それぞれのセルには鍵  {a_{i, j}} がある.
1 から順に  {p(1 \le p \le nm)} までの鍵を1つずつ得るとき, 最小移動(マンハッタン)距離を求めよ.

解法

 {x} がある位置から鍵  {x + 1} を得ることを考える.
BFS で求めれば  {O(nm)} だが  {p} が大きい時, 合計  {O(nmnm)} となってTLE.
 {x, x + 1} の位置を保存しておいて総当りすることもできるが, 鍵  {x, x + 1} があるセル数が多い時, 合計  {O(nmnm)} となってやはり TLE.

そこで, 鍵  {x, x + 1} の数によって, DP と BFS を使い分ける. それぞれの欠点を補完することができ, いい感じに計算量を抑えられて間に合う. 多分計算量は  {O(nm \sqrt {nm})} くらい.

ソース

BFSをDijkstraにしたら落ちた...
他の人々のを見ると, どうも 4 * W * H の閾値設定がまずいらしい.

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int INF = 1LL << 60;
const int dy[] = {0, 1, 0, -1}, dx[] = {1, 0, -1, 0};

int H, W, P;
vector< pair< int, int > > point[300 * 300 + 1];
int memo[300][300];
int cost[300][300];
int ps[300][300];

signed main()
{
  fill_n(*cost, 300 * 300, INF);
  
  scanf("%I64d %I64d %I64d", &H, &W, &P);
  
  for(int i = 0; i < H; i++) {
    for(int j = 0; j < W; j++) {
      scanf("%I64d", &ps[i][j]);
      point[ps[i][j]].push_back({i, j});
    }
  }
  
  for(auto& to : point[1]) {
    cost[to.first][to.second] = min(cost[to.first][to.second], to.first + to.second);
  }
        
  int ret = INF;
  for(int i = 2; i <= P; i++) {
    if(point[i - 1].size() * point[i].size() > 4 * W * H) {
      queue< pair< int, pair< int, int > > > que;
      fill_n(*memo, 300 * 300, INF);
      for(auto k : point[i - 1]) {
        memo[k.first][k.second] = cost[k.first][k.second];
        que.push({cost[k.first][k.second], k});
      }
      while(!que.empty()) {
        auto p = que.front(); que.pop();
        if(p.first > memo[p.second.first][p.second.second]) continue;
        if(ps[p.second.first][p.second.second] == i) {
          cost[p.second.first][p.second.second] = p.first;
        }
        for(int i = 0; i < 4; i++) {
          int ny = p.second.first + dy[i], nx = p.second.second + dx[i];
          if(ny < 0 || ny >= H || nx < 0 || nx >= W) continue;
          if(p.first + 1 >= memo[ny][nx]) continue;
          memo[ny][nx] = p.first + 1;
          que.push({memo[ny][nx], {ny, nx}});
        }
      }
      
    } else {
      for(auto& from : point[i - 1]) {
        for(auto& to : point[i]) {
         cost[to.first][to.second] = min(cost[to.first][to.second], cost[from.first][from.second] + abs(from.first - to.first) + abs(from.second - to.second));
        }
      }
    }
  }

  for(auto& to : point[P]) {
    ret = min(ret, cost[to.first][to.second]);
  }
  printf("%I64d\n", ret);
}