読者です 読者をやめる 読者になる 読者になる

ei1333の日記

ぺこい

Codeforces Round #349 (Div. 2)

1763 -> 1691(-72).
A, B, C, D を出して C, Dは落ちた.
なんか Reading Hard.

A. Pouring Rain

問題概要

円の直径  {d} cm の円柱状のカップがある.
カップの中には, 底から  {h} cm 水が入っている.
水を毎秒  {v} ml 飲む.
雨が降っているので毎秒  {e} cm の水がカップに入る.
カップを空にできれば, YES とともにその時間を, できなければ -1 を出力せよ.

解法

[ml] 単位に合わせて問題を整理すると良さそう.

  • 最初,  { h ( \frac{ d }{ 2 } )^{2} \pi } ml ある.
  • 毎秒,  { v } ml 減少する.
  • 毎秒,  { e ( \frac{ d }{ 2 } )^{2} \pi } ml 増加する.

増加量が減少量以上のときは, 自明に空にできない.
空にできるときも自明で, 最初の量を毎秒の減少量で割ってあげればよい.
よって,  { \frac{h ( \frac{ d }{ 2 } )^{2} \pi }{v - e ( \frac{ d }{ 2 } )^{2} \pi }} 秒となる.

ソース

#include <bits/stdc++.h>
using namespace std;
const double PI = acos(-1);
const double EPS = 1e-8;
int main()
{
  double D; 
  double H; // H * π * D^2 / 4 ml
  double V; // - V ml / s
  double E; // + E * π * D^2 / 4 ml / s
  
  cin >> D >> H >> V >> E;

  double Dec = V - E * PI * D * D / 4;
  
  if(Dec <= EPS) {
    cout << "NO" << endl;
  } else {
    cout << "YES" << endl;
    cout << fixed << setprecision(10) << H * PI * D * D / 4 / Dec << endl;
  }
}

B. Coat of Anticubism

問題概要

棒が  {N (3 \le N \le 10^{5} )} 本あり, それぞれの長さは  {h_{i} (1 \le h_{i} \le 10^{9} )} である.
ただし, それらをつなげて凸多角形を作ることはできない.
1 本棒を加えるとき, 凸多角形を作るための必要な最短の長さを求めよ.

解法

頂点を増やすと作りにくくなるので, 作るものは三角形と仮定する.
1 辺を  {h_{i}} の最大値としたとき, 三角形を作るためには他の 2 辺は, 合わせて (最大値 + 1) の長さが必要(例えば 最大値未満の長さだとつなげようとしても届かない).
よって, 足りない長さを求めればよくて,  \displaystyle \sum_{ i = 1 }^{ n } h_{i} - \max(h_{i}) + 1 が答えとなる.

ソース
#include <bits/stdc++.h>
using namespace std;
int main()
{
  int N, l[100000];
  scanf("%d", &N);
  for(int i = 0; i < N; i++) scanf("%d", &l[i]);
  cout << 2 * *max_element(l, l + N) - accumulate(l, l + N, 0) + 1 << endl;
}

C. Reberland Linguistics

問題概要

5 文字以上の文字列に, 2 文字か 3 文字からなる文字列をいくつか繋げて(ただし, 同じ文字列を2回連続で繋げることは出来ない) 文字列  {S (5 \le |S| \le 10^{5})} にしたい.
繋げる文字列として考えられるものを辞書順で出力せよ.

解法

DP. 1個前に加えた文字列を保存しておいて, 次に加える文字列が追加できるかどうか見ればよい.

ソース

本番は dpの更新が '=' だったけど, 自明に '|=' じゃないとダメだった.

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

int main()
{
  string S;
  cin >> S;
  
  S = S.substr(5);
  reverse(S.begin(), S.end());
  bool dp[10002][4] = {{}};
  
  for(int i = 0; i < S.size(); i++) {
    if(dp[i][2]) {
      int s = i + 2;
      dp[s][2] |= s + 1 < S.size() && (S[i] != S[s] || S[i + 1] != S[s + 1]);
      dp[s][3] |= s + 2 < S.size();
    }
    if(dp[i][3]) {
      int s = i + 3;
      dp[s][2] |= s + 1 < S.size();
      dp[s][3] |= s + 2 < S.size() && (S[i] != S[s] || S[i + 1] != S[s + 1] || S[i + 2] != S[s + 2]);
    }
  }

  set< string > ss;
  for(int i = 0; i < S.size(); i++) {
    if(dp[i][2]) {
      string s = "";
      s += S[i + 1];
      s += S[i];
      ss.insert(s);
    }
    if(dp[i][3]) {
      string s = "";
      s += S[i + 2];
      s += S[i + 1];
      s += S[i];
      ss.insert(s);
    }
  }
  cout << ss.size() << endl;
  for(const string& s : ss) cout << s << endl;
}

D. World Tour

問題概要

頂点数  {n(1 \le n \le 3000)}, 辺の数  {m(1 \le m \le 5000)} の有向グラフがある. 各辺の重みはすべて 1 である.
4 個の異なる頂点を選び, 順に最短経路を移動する(同じ道を何度通っても良い). このときの和を  {S} とする.
 {S} が最大となるような頂点の選び方を求めよ.

解法

まず, 全点対間最短路が欲しそうで, これは BFS で  {O(n(n + m))}.

選ぶ頂点の番号を順に,  {a, b, c, d} とおく.
 {b, c} の組を全通り試すとする.
すると,  {d} は,  {c} から最も遠い頂点となるが, 異なる頂点を選ぶという制約より最も遠い頂点が  {a, b} ではいけないことが分かる. だから, 最も遠い頂点の候補を遠い順に 3 個用意するとよい.
また,  {a} は,  {b} への経路のうち最も遠い頂点となり, これは逆辺を張れば,  {d}と同じ.

 {b, c} の全通りの組で  {a, d} の候補がそれぞれ高々 3 通りなので, 計算量は  {O(n(n + m) + 9n^{2})} となって間に合う.

ソース

冷静になれば当たり前だよね....

#include <bits/stdc++.h>
using namespace std;
typedef pair< int, int > Pi;

vector< int > graph[3000], rgraph[3000];
int g[3000][3000];
set< Pi > qq[2][3000];
int main()
{
  int N, M;
  cin >> N >> M;
  for(int i = 0; i < M; i++) {
    int u, v;
    cin >> u >> v;
    --u, --v;
    graph[u].push_back(v);
    rgraph[v].push_back(u);
  }

  for(int l = 0; l < 2; l++, swap(graph, rgraph)) {
    memset(g, -1, sizeof(g));
    for(int i = 0; i < N; i++) {
      queue< int > que;
      que.push(i);
      g[i][i] = 0;
      while(!que.empty()) {
        int p = que.front(); que.pop();
        for(int to : graph[p]) {
          if(g[i][to] == -1) {
            que.push(to);
            g[i][to] = g[i][p] + 1;
            qq[l][i].insert({g[i][to], to});
            while(qq[l][i].size() > 3) qq[l][i].erase(qq[l][i].begin());
          }
        }
      }
    }
  }

  int ret = -114;
  int aa, bb, cc, dd;
  
  for(int i = 0; i < N; i++) {
    for(int j = 0; j < N; j++) {
      if(i == j || g[j][i] == -1) continue;
      for(const Pi& a : qq[1][i]) {
        if(i == a.second || j == a.second) continue;
        for(const Pi& b : qq[0][j]) {
          if(i == b.second || j == b.second || a.second == b.second) continue;
          if(ret < g[j][i] + a.first + b.first) {
            ret = g[j][i] + a.first + b.first;
            aa = a.second, bb = i, cc = j, dd = b.second;
          }
        }
      }
    }
  }
  cout << aa + 1 << " " << bb + 1 << " " << cc + 1 << " " << dd + 1 << endl;
}