Removing People 题解

前言

题目链接:Atcoder洛谷

题意简述

n 人站成一个圆圈,按顺时针方向依次为 1,2,,n

每个人面对的方向由长度为 n 的字符串 S 给出。对于第 i 个人,如果 Si=L,则 i 面向逆时针方向。如果 Si=R,则面向顺时针方向。

重复 n1 次以下操作:以相等的概率从剩余的人中选择一个,并从圆中移除离被选中的人最近的人。这样做的代价等于被选中的人到被移除的人的距离。

定义从 ijij)的距离 dis(i,j) 为,i 按照其方向行走多少步能够到达 j

n1 次操作后代价之和的期望值,对 M=998244353 取模。

2n300

题目分析

期望类题目,我们学过的算法好像只有 DP 吧?考虑 DP。状态如何设计呢?移除一个人,难道我们要把 n 个人还在不在压到状态里吗?显然不行。正难则反,我们考虑从只有 1 个人开始,逐渐往里面加入一个人。

加入 i,在反转操作前是删除 i,说明我们选择了一个 j,且 ij 朝着其方向前进遇到的第一个人,将 ji 的距离累加到答案中去。

我们注意到,对于 ji 中间的 k,如果加入 k,只可能是选择了 ij 其中的一个。加入 k 之后,分成了两个区间,就又形成了规模更小的子问题,且问题仅和区间两端有关!

考虑区间 DP。设 fl,r 表示 lr 中,最初仅有 lr 加入了,经过了若干次操作,把中间的所有人都加入的期望价值。可是,如果要算期望,我们需要知道目前已经放下了多少个人,不然算不了概率,而这是我们状态之外的东西。

那就别记期望了吧,把期望变成价值和比上方案数。方案数显然是 n!,那么我们 DP 价值和,即记 fl,r 表示把 lr 中间的放下的价值和,为了转移需要再记一个方案数 gl,r

先来考虑 g 的转移。先枚举 k 表示第一个放下的,这里需要注意,我们是选择 lr 的哪一个,导致 k 被放下的呢?如果合法,都有可能,所以这里的方案数为 [Sl=R]+[Sr=L]。放下后,两个子问题的方案数直接相乘 gl,kgk,r 就行了吗?并不是,因为我们可以交叉着放置,即先放置左边区间的某一个,再放置右边的某一个,以此类推。这一部分的方案数是合并两个有序序列的方案数,设 x=dis(l,k)1,y=dis(k,r)1,即合并两个长度分别为 x,y 的有序序列,考虑最终长度为 x+y 的序列中选出 x 个位置作为其中一个有序序列,方案数是 (x+yx)

说了这么多,其实就是一个转移方程:

gl,r=([Sl=R]+[Sr=L])gl,kgk,r(dis(l,r)2dis(l,k)1)

f 的转移很类似,如果是选择 l 导致 k 被加入:

fl,r[Sl=R](dis(l,k)gl,kgk,r+gl,kfk,r+gk,rfl,k)(dis(l,r)2dis(l,k)1)

选择 r 同理有:

fl,r[Sr=L](dis(k,r)gl,kgk,r+gl,kfk,r+gk,rfl,k)(dis(l,r)2dis(l,k)1)

注意到,之所以我一直避免 ji 之类的出现,是因为这是一个环,读者要处理好环的问题。

DP 初值考虑相邻的两项的 g=1。答案即为 fi,i+nn!

时间复杂度:Θ(n3)

代码

展开取模板子
namespace Mod_Int_Class {
template <typename T, typename _Tp>
constexpr bool in_range(_Tp val) {
return std::numeric_limits<T>::min() <= val && val <= std::numeric_limits<T>::max();
}
template <typename _Tp, typename = std::enable_if_t<std::is_integral<_Tp>::value>>
static constexpr inline bool is_prime(_Tp val) {
if (val < 2) return false;
for (_Tp i = 2; i * i <= val; ++i)
if (val % i == 0)
return false;
return true;
}
template <auto _mod = 998244353, typename T = int, typename S = long long>
class Mod_Int {
static_assert(in_range<T>(_mod), "mod must in the range of type T.");
static_assert(std::is_integral<T>::value, "type T must be an integer.");
static_assert(std::is_integral<S>::value, "type S must be an integer.");
public:
constexpr Mod_Int() noexcept = default;
template <typename _Tp, typename = std::enable_if_t<std::is_integral<_Tp>::value>>
constexpr Mod_Int(_Tp v) noexcept: val(0) {
if (0 <= T(v) && T(v) < mod) val = v;
else val = (T(v) % mod + mod) % mod;
}
constexpr T const& raw() const {
return this -> val;
}
static constexpr T mod = _mod;
template <typename _Tp, typename = std::enable_if_t<std::is_integral<_Tp>::value>>
constexpr friend Mod_Int pow(Mod_Int a, _Tp p) {
return a ^ p;
}
constexpr friend Mod_Int sub(Mod_Int a, Mod_Int b) {
return a - b;
}
constexpr friend Mod_Int& tosub(Mod_Int& a, Mod_Int b) {
return a -= b;
}
constexpr friend Mod_Int add(Mod_Int a) { return a; }
template <typename... args_t>
constexpr friend Mod_Int add(Mod_Int a, args_t... args) {
return a + add(args...);
}
constexpr friend Mod_Int mul(Mod_Int a) { return a; }
template <typename... args_t>
constexpr friend Mod_Int mul(Mod_Int a, args_t... args) {
return a * mul(args...);
}
template <typename... args_t>
constexpr friend Mod_Int& toadd(Mod_Int& a, args_t... b) {
return a = add(a, b...);
}
template <typename... args_t>
constexpr friend Mod_Int& tomul(Mod_Int& a, args_t... b) {
return a = mul(a, b...);
}
template <T __mod = mod, typename = std::enable_if_t<is_prime(__mod)>>
static constexpr inline T inv(T a) {
assert(a != 0);
return _pow(a, mod - 2);
}
constexpr Mod_Int& operator + () const {
return *this;
}
constexpr Mod_Int operator - () const {
return _sub(0, val);
}
constexpr Mod_Int inv() const {
return inv(val);
}
constexpr friend inline Mod_Int operator + (Mod_Int a, Mod_Int b) {
return _add(a.val, b.val);
}
constexpr friend inline Mod_Int operator - (Mod_Int a, Mod_Int b) {
return _sub(a.val, b.val);
}
constexpr friend inline Mod_Int operator * (Mod_Int a, Mod_Int b) {
return _mul(a.val, b.val);
}
constexpr friend inline Mod_Int operator / (Mod_Int a, Mod_Int b) {
return _mul(a.val, inv(b.val));
}
template <typename _Tp, typename = std::enable_if_t<std::is_integral<_Tp>::value>>
constexpr friend inline Mod_Int operator ^ (Mod_Int a, _Tp p) {
return _pow(a.val, p);
}
constexpr friend inline Mod_Int& operator += (Mod_Int& a, Mod_Int b) {
return a = _add(a.val, b.val);
}
constexpr friend inline Mod_Int& operator -= (Mod_Int& a, Mod_Int b) {
return a = _sub(a.val, b.val);
}
constexpr friend inline Mod_Int& operator *= (Mod_Int& a, Mod_Int b) {
return a = _mul(a.val, b.val);
}
constexpr friend inline Mod_Int& operator /= (Mod_Int& a, Mod_Int b) {
return a = _mul(a.val, inv(b.val));
}
template <typename _Tp, typename = std::enable_if_t<std::is_integral<_Tp>::value>>
constexpr friend inline Mod_Int& operator ^= (Mod_Int& a, _Tp p) {
return a = _pow(a.val, p);
}
constexpr friend inline bool operator == (Mod_Int a, Mod_Int b) {
return a.val == b.val;
}
constexpr friend inline bool operator != (Mod_Int a, Mod_Int b) {
return a.val != b.val;
}
constexpr Mod_Int& operator ++ () {
this -> val + 1 == mod ? this -> val = 0 : ++this -> val;
return *this;
}
constexpr Mod_Int& operator -- () {
this -> val == 0 ? this -> val = mod - 1 : --this -> val;
return *this;
}
constexpr Mod_Int operator ++ (int) {
Mod_Int res = *this;
this -> val + 1 == mod ? this -> val = 0 : ++this -> val;
return res;
}
constexpr Mod_Int operator -- (int) {
Mod_Int res = *this;
this -> val == 0 ? this -> val = mod - 1 : --this -> val;
return res;
}
friend std::istream& operator >> (std::istream& is, Mod_Int<mod, T, S>& x) {
T ipt;
return is >> ipt, x = ipt, is;
}
friend std::ostream& operator << (std::ostream& os, Mod_Int<mod, T, S> x) {
return os << x.val;
}
protected:
T val;
static constexpr inline T _add(T a, T b) {
return a >= mod - b ? a + b - mod : a + b;
}
static constexpr inline T _sub(T a, T b) {
return a < b ? a - b + mod : a - b;
}
static constexpr inline T _mul(T a, T b) {
return static_cast<S>(a) * b % mod;
}
template <typename _Tp, typename = std::enable_if_t<std::is_integral<_Tp>::value>>
static constexpr inline T _pow(T a, _Tp p) {
T res = 1;
for (; p; p >>= 1, a = _mul(a, a))
if (p & 1) res = _mul(res, a);
return res;
}
};
using mint = Mod_Int<>;
constexpr mint operator ""_m (unsigned long long x) {
return mint(x);
}
constexpr mint operator ""_mod (unsigned long long x) {
return mint(x);
}
}
#include <cstdio>
#include <iostream>
#include <limits>
using namespace std;
using namespace Mod_Int_Class;
const int N = 310;
int n;
char S[N];
mint frac[N], Inv[N], ifrac[N];
mint f[N][N], g[N][N];
inline mint C(int n, int m) {
return frac[n] * ifrac[m] * ifrac[n - m];
}
signed main() {
scanf("%d%s", &n, S + 1);
for (int i = 1; i < n; ++i) g[i][i + 1] = 1;
g[n][1] = 1, frac[0] = ifrac[0] = 1;
for (int i = 1; i <= n; ++i) {
frac[i] = frac[i - 1] * i;
Inv[i] = i == 1 ? 1 : 0_mod - (mint::mod / i) * Inv[mint::mod % i];
ifrac[i] = ifrac[i - 1] * Inv[i];
}
for (int len = 3; len <= n + 1; ++len)
for (int l = 1; l <= n; ++l) {
int r = l + len - 1;
int rr = r > n ? r - n : r;
for (int k = l + 1; k < r; ++k) {
int kk = k > n ? k - n : k;
mint o = C(r - l - 2, k - l - 1);
g[l][rr] += g[l][kk] * g[kk][rr] * o;
if (S[l] == 'R') {
f[l][rr] += ((k - l) * g[l][kk] * g[kk][rr] + g[l][kk] * f[kk][rr] + g[kk][rr] * f[l][kk]) * o;
}
if (S[rr] == 'L') {
f[l][rr] += ((r - k) * g[l][kk] * g[kk][rr] + g[l][kk] * f[kk][rr] + g[kk][rr] * f[l][kk]) * o;
}
}
g[l][rr] *= (S[l] == 'R') + (S[rr] == 'L');
}
mint sum = 0;
for (int i = 1; i <= n; ++i) sum += f[i][i];
sum *= ifrac[n];
printf("%d\n", sum.raw());
return 0;
}
posted @   XuYueming  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示