The 2016 ACM-ICPC Asia Shenyang Regional Contest

A.Thickest Burger

签到题,输出$max(a+2b,2a+b)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void solve() {
  int a, b;
  cin >> a >> b;
  cout << max(a + 2 * b, 2 * a + b) << '\n';
}

int main(int argc, char *argv[]) {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout << fixed << setprecision(10);

  int o_o;
  for (cin >> o_o; o_o; --o_o) solve();

  return 0;
}

B.Relative atomic mass

签到题,计算分子量。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void solve() {
  string s;
  cin >> s;
  int ans = 0;
  for (char &ch : s) {
    if (ch == 'C') {
      ans += 12;
    } else if (ch == 'H') {
      ++ans;
    } else {
      ans += 16;
    }
  }
  cout << ans << '\n';
}

int main(int argc, char *argv[]) {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout << fixed << setprecision(10);

  int o_o;
  for (cin >> o_o; o_o; --o_o) solve();

  return 0;
}

C.Recursive sequence

矩阵快速幂经典题

$ f_n = f_{n - 1} + 2 f_{n - 2} + n^4 $

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const int MAXN = 7;
const ull MOD = 2147493647;

typedef ull Mat[MAXN][MAXN];

void mul(Mat &a, const Mat &b) {
  static Mat c;
  ZERO(c);
  FOR2(i, 0, MAXN, j, 0, MAXN)
  FOR(k, 0, MAXN) c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % MOD;
  memcpy(a, c, sizeof(c));
}

Mat A, B;

const Mat P = {{1, 2, 1, 4, 6, 4, 1}, {1, 0, 0, 0, 0, 0, 0},
               {0, 0, 1, 4, 6, 4, 1}, {0, 0, 0, 1, 3, 3, 1},
               {0, 0, 0, 0, 1, 2, 1}, {0, 0, 0, 0, 0, 1, 1},
               {0, 0, 0, 0, 0, 0, 1}};

void solve() {
  int n, a, b;
  cin >> n >> a >> b;
  if (n < 3) {
    cout << (n == 1 ? a : b) << '\n';
    return;
  }
  ZERO(A);
  MEMCPY(B, P, sizeof(P));
  FOR(i, 0, MAXN) A[i][i] = 1;
  for (n -= 2; n; n >>= 1, mul(B, B))
    if (n & 1) mul(A, B);
  ull ans = 0;
  const ull L[] = {1ULL * b, 1ULL * a, 16, 8, 4, 2, 1};
  FOR(i, 0, MAXN) ans = (ans + A[0][i] * L[i]) % MOD;
  cout << ans << '\n';
}

int main(int argc, char *argv[]) {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout << fixed << setprecision(10);

  int o_o;
  for (cin >> o_o; o_o; --o_o) solve();

  return 0;
}

E.Counting Cliques

题意:计算一个图中大小为 S 的完全子图的个数,每个点的度不超过 20。

因为每个点的度很小,所以可以暴力 dfs。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const int MAXN = 100 + 3;

vi adj[MAXN];
bool connected[MAXN][MAXN], vis[MAXN];
int n, m, s, top, stk[MAXN], ans;

inline bool check(int u) {
  FOR(i, 0, top)
    if (!connected[stk[i]][u]) return false;
  return true;
}

void dfs(int u) {
  if (top == s) return (void)(++ans);
  if (n - u + top < s) return;
  FOR(i, 0, adj[u].size()) {
    if (vis[adj[u][i]] || !check(adj[u][i])) continue;
    vis[adj[u][i]] = true;
    stk[top++] = adj[u][i];
    dfs(adj[u][i]);
    --top;
    vis[adj[u][i]] = false;
  }
}

void solve() {
  cin >> n >> m >> s;
  FOR(i, 0, n) adj[i].clear(), vis[i] = false;
  FOR2(i, 0, n, j, 0, n) connected[i][j] = false;
  for (int u, v; m; --m) {
    cin >> u >> v;
    if (--u > --v) swap(u, v);
    adj[u].push_back(v);
    connected[u][v] = true;
  }
  ans = 0;
  FOR(i, 0, n) {
    top = 1;
    stk[0] = i;
    vis[i] = true;
    dfs(i);
  }
  cout << ans << '\n';
}

int main(int argc, char *argv[]) {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout << fixed << setprecision(10);

  int o_o;
  for (cin >> o_o; o_o; --o_o) solve();

  return 0;
}

G.Do not pour out

题意:给你一个杯子,直径为 2,高为 2,水高为 d,现将杯子倾斜,使得水恰好不洒出,问 此时水表面面积。

显然表面为椭圆或半椭圆,分为两种情况:

  1. 水面经过杯底,通过二分得出水在杯底的面积,然后计算。
  2. 水面越过杯底,可以较轻松的算出长短半轴。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

const double EPS = 1e-12;
const double PI = acos(-1.0);

inline double cal_v(double x) {
  double t = acos(1 - x);
  double v = sin(t) - sin(t) * sin(t) * sin(t) / 3 - t * cos(t);
  return v * 2 / x;
}

inline double ract(double a, double x) { return (x + sin(2 * x) / 2) * a / 2; }

inline double area(double a, double x) {
  return 2 * (ract(a, PI / 2) - ract(a, asin(x / a)));
}

void solve() {
  double d;
  cin >> d;
  if (d == 0) {
    cout << .0 << '\n';
    return;
  }
  if (d > 1) {
    double h = 2 * d - 2;
    double a = sqrt(4 + (2 - h) * (2 - h)) / 2;
    cout << PI * a << '\n';
    return;
  }
  double l = 0, r = 2, v = PI * d;
  for (int step = 0; step < 100; ++step) {
    double mid = (l + r) / 2;
    if (cal_v(mid) > v + EPS) {
      r = mid;
    } else {
      l = mid;
    }
  }
  int sign = l < 1 ? -1 : 1;
  double len = sqrt(l * l + 4);
  double h = sqrt(2 * l - l * l);
  double a = len / (1 + sign * sqrt(1 - h * h));
  double x = a - len;
  cout << area(a, x) << '\n';
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout << fixed << setprecision(5);

  int o_o;
  for (cin >> o_o; o_o; --o_o) solve();
  return 0;
}

H.Guessing the Dice Roll

题意:一群人在完骰子游戏,每个人有一个特定的序列,当掷到这个序列时对应的人获胜, 问每个人获胜的概率。

在 AC 自动机上做概率 dp,由于 AC 自动机不是一个 DAG 所以要用高斯消元求解。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
const int MAX_NODE = 103;

int ch[MAX_NODE][6], fail[MAX_NODE], node_c;

int add_char(int u, int id) {
  if (ch[u][id] < 0) ch[u][id] = node_c++;
  return ch[u][id];
}

void build_acam() {
  queue<int> que;
  FOR(i, 0, 6)
  if (~ch[0][i]) {
    que.push(ch[0][i]);
    fail[ch[0][i]] = 0;
  } else {
    ch[0][i] = 0;
  }
  while (!que.empty()) {
    int u = que.front();
    que.pop();
    FOR(i, 0, 6)
    if (~ch[u][i]) {
      que.push(ch[u][i]);
      fail[ch[u][i]] = ch[fail[u]][i];
    } else {
      ch[u][i] = ch[fail[u]][i];
    }
  }
}

const double EPS = 1e-9;
const int MAXN = MAX_NODE;
double a[MAXN][MAXN], x[MAXN];
int equ, var;

int gauss() {
  int i, j, k, col, max_r;
  for (k = 0, col = 0; k < equ && col < var; k++, col++) {
    max_r = k;
    for (i = k + 1; i < equ; i++)
      if (fabs(a[i][col]) > fabs(a[max_r][col])) max_r = i;
    if (fabs(a[max_r][col]) < EPS) return 0;

    if (k != max_r) {
      for (j = col; j < var; j++) swap(a[k][j], a[max_r][j]);
      swap(x[k], x[max_r]);
    }

    x[k] /= a[k][col];
    for (j = col + 1; j < var; j++) a[k][j] /= a[k][col];
    a[k][col] = 1;

    for (i = k + 1; i < equ; i++)
      if (i != k) {
        x[i] -= x[k] * a[i][col];
        for (j = col + 1; j < var; j++) a[i][j] -= a[k][j] * a[i][col];
        a[i][col] = 0;
      }
  }

  for (col = equ - 1, k = var - 1; ~col; --col, --k) {
    if (fabs(a[col][k]) > 0) {
      for (i = 0; i < k; ++i) {
        x[i] -= x[k] * a[i][col];
        for (j = col + 1; j < var; j++) a[i][j] -= a[k][j] * a[i][col];
        a[i][col] = 0;
      }
    }
  }

  return 1;
}

int ed_node[10], id[MAXN];

void solve() {
  int n, l, t;
  cin >> n >> l;

  node_c = 1;
  memset(ch, -1, sizeof(ch));
  memset(id, -1, sizeof(id));
  FOR(i, 0, n) {
    ed_node[i] = 0;
    FOR(j, 0, l) cin >> t, ed_node[i] = add_char(ed_node[i], t - 1);
    id[ed_node[i]] = i;
  }
  build_acam();

  FOR(step, 0, n) {
    equ = var = node_c;
    FOR(i, 0, equ) {
      x[i] = 0;
      FOR(j, 0, var) a[i][j] = 0;
    }
    FOR(i, 0, node_c) {
      a[i][i] = 1;
      if (~id[i]) {
        x[i] = id[i] == step ? 1 : 0;
      } else {
        FOR(j, 0, 6) a[i][ch[i][j]] += -1.0 / 6;
      }
    }

    gauss();
    if (step) cout << ' ';
    cout << x[0];
  }
  cout << '\n';
}

int main(int argc, char *argv[]) {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout << fixed << setprecision(6);

  int o_o;
  for (cin >> o_o; o_o; --o_o) solve();

  return 0;
}

I.The Elder

题意:给定一棵带边权树,根节点为 1,每个节点要想给根节点通过记者传递信息。记者经 一段路耗时为长度的平方。记者间也可以传递信息,耗时为 P,问在最优方案下,一个信息 传递到根节点最长耗时。

定义$ dpi $ 表示 i 节点传递到根节点的最短耗时,规定$ dp{root}=-P $。有如下转移方程 $dp_u = dp_v + dist(u, v)^2 + P$,v 是 u 的一个祖先。

然后就是在树上做斜率优化。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
typedef long long ll;
typedef pair<int, int> pii;
const int MAXN = 1e5 + 5;

vector<pii> adj[MAXN];
ll dp[MAXN], d[MAXN];
int n, p, q[MAXN], head, tail;

inline ll S(int a, int b) { return (d[b] - d[a]) << 1; }
inline ll G(int a, int b) { return dp[b] - dp[a] + d[b] * d[b] - d[a] * d[a]; }

void dfs(int u, int from) {
  vector<int> dhead, dtail;
  if (u ^ 1) {
    while (head + 2 <= tail &&
           S(q[head + 1], q[head]) * d[u] <= G(q[head + 1], q[head]))
      dhead.push_back(q[head++]);
    int v = q[head];
    dp[u] = dp[v] + p + (d[u] - d[v]) * (d[u] - d[v]);
  }
  while (head + 2 <= tail &&
         G(u, q[tail - 1]) * S(q[tail - 1], q[tail - 2]) <=
             G(q[tail - 1], q[tail - 2]) * S(u, q[tail - 1]))
    dtail.push_back(q[--tail]);
  q[tail++] = u;
  for (pii &e : adj[u]) {
    if (e.first == from) continue;
    d[e.first] = d[u] + e.second;
    dfs(e.first, u);
  }
  --tail;
  for (int i = dtail.size() - 1; ~i; --i) q[tail++] = dtail[i];
  for (int i = dhead.size() - 1; ~i; --i) q[--head] = dhead[i];
}

void solve() {
  cin >> n >> p;
  for (int i = 1; i <= n; ++i) adj[i].clear();
  for (int i = 1, u, v, w; i < n; ++i) {
    cin >> u >> v >> w;
    adj[u].emplace_back(v, w);
    adj[v].emplace_back(u, w);
  }
  dp[1] = -p;
  head = tail = 0;
  dfs(1, 1);

  ll ans = 0;
  for (int i = 1; i <= n; ++i)
    if (dp[i] > ans) ans = dp[i];
  cout << ans << '\n';
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);

  int o_o;
  for (cin >> o_o; o_o; --o_o) solve();

  return 0;
}
comments powered by Disqus