CodeForces Gym 101955 简要题解

本文详细解读了CodeForces Gym 101955中的算法问题,包括Sockpuppets的祖先匹配、Sequences Generator的概率计算、Insertion Sort的排序分析、Diameter of a Tree的树直径求解、The Kouga Ninja Scrolls的区间最大次大值维护等,深入探讨了解题策略和技巧。
摘要由CSDN通过智能技术生成

Sockpuppets

建出trie树,那么匹配的东西一定是祖先关系。

f ( x , a , b ) f(x,a,b) f(x,a,b) 表示考虑了 x x x 的子树,祖先有 a a a 个小号匹配子树,子树有 b b b 个小号匹配祖先的方案数,背包转移即可。

#include <bits/stdc++.h>

using namespace std;

const int N = 23456;
const int ALPHA = 26;
const int md = 1e9 + 7;

int n, m, root, total, type[N], go[N][ALPHA], dp[N][11][21];
char str[N];

inline void add(int &x, int y) {
   
  x += y;
  if (x >= md) {
   
    x -= md;
  }
}

inline int mul(int x, int y) {
   
  return (long long)x * y % md;
}

inline int new_node() {
   
  ++total;
  for (int i = 0; i < ALPHA; ++i) {
   
    go[total][i] = 0;
  }
  type[total] = 0;
  return total;
}

void dfs(int x) {
   
  static int temp[11][21];
  memset(dp[x], 0, sizeof dp[x]);
  dp[x][0][0] = 1;
  for (int i = 0; i < ALPHA; ++i) {
   
    int y = go[x][i];
    if (y) {
   
      dfs(y);
    }
  }
  for (int i = 0; i < ALPHA; ++i) {
   
    int y = go[x][i];
    if (y) {
   
      for (int a = 0; a <= 10; ++a) {
   
        for (int b = 0; b <= 20; ++b) {
   
          if (!dp[x][a][b]) {
   
            continue;
          }
          for (int aa = 0; aa <= 10 - a; ++aa) {
   
            for (int bb = 0; bb <= 20 - b; ++bb) {
   
              if (dp[y][aa][bb]) {
   
                add(temp[a + aa][b + bb], mul(dp[x][a][b], dp[y][aa][bb]));
              }
            }
          }
        }
      }
      for (int a = 0; a <= 10; ++a) {
   
        for (int b = 0; b <= 20; ++b) {
   
          dp[x][a][b] = temp[a][b];
          temp[a][b] = 0;
        }
      }
    }
  }
  if (type[x] == 1) {
   
    for (int a = 0; a <= 10; ++a) {
   
      for (int b = 0; b <= 20; ++b) {
   
        if (!dp[x][a][b]) {
   
          continue;
        }
        add(temp[a][b], dp[x][a][b]);
        if (a + 1 <= 10) {
   
          add(temp[a + 1][b], dp[x][a][b]);
        }
        if (b - 1 >= 0) {
   
          add(temp[a][b - 1], mul(b, dp[x][a][b]));
        }
        if (a + 2 <= 10) {
   
          add(temp[a + 2][b], mul(md + 1 >> 1, dp[x][a][b]));
        }
        if (a + 1 <= 10 && b - 1 >= 0) {
   
          add(temp[a + 1][b - 1], mul(b, dp[x][a][b]));
        }
        if (b - 2 >= 0) {
   
          add(temp[a][b - 2], mul(b * (b - 1) / 2, dp[x][a][b]));
        }
      }
    }
    for (int a = 0; a <= 10; ++a) {
   
      for (int b = 0; b <= 20; ++b) {
   
        dp[x][a][b] = temp[a][b];
        temp[a][b] = 0;
      }
    }
  } else if (type[x] == 2) {
   
    for (int a = 0; a <= 10; ++a) {
   
      for (int b = 0; b <= 20; ++b) {
   
        if (!dp[x][a][b]) {
   
          continue;
        }
        add(temp[a][b], dp[x][a][b]);
        if (a - 1 >= 0) {
   
          add(temp[a - 1][b], mul(a, dp[x][a][b]));
        }
        if (b + 1 <= 20) {
   
          add(temp[a][b + 1], dp[x][a][b]);
        }
      }
    }
    for (int a = 0; a <= 10; ++a) {
   
      for (int b = 0; b <= 20; ++b) {
   
        dp[x][a][b] = temp[a][b];
        temp[a][b] = 0;
      }
    }
  }
}

int main() {
   
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
   
    scanf("%d %d", &n, &m);
    total = 0;
    root = new_node();
    for (int i = 1; i <= n; ++i) {
   
      scanf("%s", str);
      int x = root;
      for (int j = 0; str[j]; ++j) {
   
        if (!go[x][str[j] - 'a']) {
   
          go[x][str[j] - 'a'] = new_node();
        }
        x = go[x][str[j] - 'a'];
      }
      type[x] = 1;
    }
    for (int i = 1; i <= m; ++i) {
   
      scanf("%s", str);
      int x = root;
      for (int j = 0; str[j]; ++j) {
   
        if (!go[x][str[j] - 'a']) {
   
          go[x][str[j] - 'a'] = new_node();
        }
        x = go[x][str[j] - 'a'];
      }
      type[x] = 2;
    }
    dfs(root);
    printf("Case #%d: %d\n", ttt, dp[root][0][0]);
  }
  return 0;
}

Sequences Generator

先默认每个位置是最大概率的数,每次失配会让概率乘最多 1 2 \frac{1}{2} 21 ,用后缀数组暴力匹配,当概率小于 e p s eps eps 时直接返回 0 0 0 即可。

#include <bits/stdc++.h>

using namespace std;

typedef double ld;

const int N = 623456;
const int LOG = 20;
const ld eps = 1e-12;

int n, m, low[N], high[N], prob[N][10];
ld sum[N];

struct suffix_array_t {
   
  int n, s[N], rank[N], array[N], height[N], length[N], rmq[LOG][N];

  void init() {
   
    static int temp[N], first[N], second[N], number[N];
    for (int i = 1; i <= n; ++i) {
   
      ++number[s[i]];
    }
    for (int i = 1; i <= n; ++i) {
   
      number[i] += number[i - 1];
    }
    for (int i = 1; i <= n; ++i) {
   
      array[number[s[i]]--] = i;
    }
    for (int i = 1; i <= n; ++i) {
   
      number[i] = 0;
    }
    rank[array[1]] = 1;
    for (int i = 2; i <= n; ++i) {
   
      rank[array[i]] = rank[array[i - 1]] + (s[array[i]] != s[array[i - 1]]);
    }
    for (int length = 1; rank[array[n]] != n; length <<= 1) {
   
      for (int i = 1; i <= n; ++i) {
   
        ++number[second[i] = i + length > n ? 0 : rank[i + length]];
      }
      for (int i = 1; i <= n; ++i) {
   
        number[i] += number[i - 1];
      }
      for (int i = 1; i <= n; ++i) {
   
        temp[number[second[i]]--] = i;
      }
      for (int i = 0; i <= n; ++i) {
   
        number[i] = 0;
      }
      for (int i = 1; i <= n; ++i) {
   
        ++number[first[i] = rank[i]];
      }
      for (int i = 1; i <= n; ++i) {
   
        number[i] += number[i - 1];
      }
      for (int i = n; i; --i) {
   
        array[number[first[temp[i]]]--] = temp[i];
      }
      for (int i = 1; i <= n; ++i) {
   
        number[i] = 0;
      }
      rank[array[1]] = 1;
      for (int i = 2; i <= n; ++i) {
   
        rank[array[i]] = rank[array[i - 1]] + (first[array[i]] != first[array[i - 1]] || second[array[i]] != second[array[i - 1]]);
      }
    }
    for (int i = 1, j = 0; i <= n; ++i) {
   
      for (j -= j > 0; rank[i] != 1 && i + j <= n && array[rank[i] - 1] + j <= n && s[i + j] == s[array[rank[i] - 1] + j]; ++j);
      height[rank[i]] = j;
    }
    for (int i = 2; i <= n; ++i) {
   
      length[i] = length[i >> 1] + 1;
    }
    for (int i = 1; i <= n; ++i) {
   
      rmq[0][i] = height[i];
    }
    for (int i = 1; i < LOG; ++i) {
   
      for (int j = 1; j + (1 << i) - 1 <= n; ++j) {
   
        rmq[i][j] = min(rmq[i - 1][j], rmq[i - 1][j + (1 << i - 1)]);
      }
    }
  }

  int lcp(int x, int y) {
   
    if (x == y) {
   
      return n - x + 1;
    }
    x = rank[x];
    y = rank[y];
    if (x > y) {
   
      swap(x, y);
    }
    ++x;
    int k = length[y - x];
    return min(rmq[k][x], rmq[k][y - (1 << k) + 1]);
  }
} suffix_array;

ld solve(int l, int r, int ll, int rr, ld value) {
   
  if (value < eps) {
   
    return 0;
  }
  if (l > r) {
   
    return value;
  }
  int lcp = suffix_array.lcp(l, ll);
  if (lcp >= r - l + 1) {
   
    return value * exp
您提供的链接是Codeforces的一个问题,问题编号为104377。Codeforces是一个知名的在线编程竞赛平台,经常举办各种编程比赛和训练。GymCodeforces的一个扩展包,用于组织私人比赛和训练。您提供的链接指向了一个问题的页面,但具体的问题内容和描述无法通过链接获取。如果您有具体的问题或需要了解关于Codeforces Gym的更多信息,请提供更详细的信息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [http://codeforces.com/gym/100623/attachments E题](https://blog.csdn.net/weixin_30820077/article/details/99723867)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [http://codeforces.com/gym/100623/attachments H题](https://blog.csdn.net/weixin_38166726/article/details/99723856)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [CodeforcesPP:Codeforces扩展包](https://download.csdn.net/download/weixin_42101164/18409501)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值