[题解]Viva la vida!

\[\color{red}{\text{校长者,真神人也,左马桶,右永神,会执利笔破邪炁,何人当之?}} \\ \begin{array}{|} \hline \color{pink}{\text{The principal is really a god}} \\ \color{pink}{\text{with a closestool on the left and Yongshen on the right}} \\ \color{pink}{\text{holding a sharp pen to pierce the truth}} \\ \color{pink}{\text{Who can resist him? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{green}{\text{校長は本当に神であり、左側にトイレ、右側にヨンシェンがあり}} \\ \color{green}{\text{鋭いペンを持って真実を突き刺している。誰が彼に抵抗できるだろうか? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{lightblue}{\text{Le principal est vraiment un dieu}} \\ \color{lightblue}{\text{avec des toilettes à gauche et Yongshen à droite}} \\ \color{lightblue}{\text{tenant un stylo pointu pour percer la vérité}} \\ \color{lightblue}{\text{Qui peut lui résister ? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{purple}{\text{Der Direktor ist wirklich ein Gott}} \\ \color{purple}{\text{mit einer Toilette links und Yongshen rechts}} \\ \color{purple}{\text{der einen spitzen Stift hält}} \\ \color{purple}{\text{um die Wahrheit zu durchdringen.}} \\ \color{purple}{\text{Wer kann ihm widerstehen? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{cyan}{\text{Principalis deus est, Yongshen a dextris cum latrina}} \\ \color{cyan}{\text{acuto stylo ad perforandum veritatem: quis resistet ei? }} \\ \hline \end{array} \\ \color{red}{\text{对曰:“无人,狗欲当之,还请赐教!”}} \\ \newcommand\brak[1]{\left({#1}\right)} \newcommand\Brak[1]{\left\{{#1}\right\}} \newcommand\d[0]{\text{d}} \newcommand\string[2]{\genfrac{\{}{\}}{0pt}{}{#1}{#2}} \newcommand\down[2]{{#1}^{\underline{#2}}} \newcommand\ddiv[2]{\left\lfloor\frac{#1}{#2}\right\rfloor} \newcommand\udiv[2]{\left\lceil\frac{#1}{#2}\right\rceil} \newcommand\lcm[0]{\operatorname{lcm}} \newcommand\set[1]{\left\{{#1}\right\}} \newcommand\ceil[1]{\left\lceil{#1}\right\rceil} \newcommand\floor[1]{\left\lfloor{#1}\right\rfloor} \newcommand\rhs[1]{\;\text{Rhs}\;#1} \newcommand\lhs[1]{\;\text{Lhs}\;#1} \newcommand\Vec[1]{\vec{\mathbf{#1}}} \newcommand\rank[0]{\text{rank}} \]




  \(\mathcal{Back\;To\;The\;Menu}\).

2022-03-11 Viva la vida!

  T1 拿来搞笑的吧?T2 知道 \(\mathcal O(n^2)\) 还转不到正解上面去......T3 就没怎么想了。

王 / King

I used to roll the dice

过去我常常孤注一掷


Feel the fear in my enemy's eyes

尽情品味惊恐在死敌瞳孔绽开


Listen as the crowd would sing:

欣然倾听百姓高歌喝彩


"Now the old king is dead! Long live the king!"

“先王亡矣!新王万代!”

  从 \(0\) 开始倒着考虑,那么原题就相当于分别用长度为 \(1,2,3,\cdots\) 的全 \(1\) 串去异或当前的串,也可以不异或。

  做一遍 dijkstra 就行了,同时不难发现一定有解且小于等于 \(16\),因为当长度为 \(16\) 时线性基满秩。

/** @author __Elaina__ */

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

// #define USING_FREAD
// #define NDEBUG
#include <cassert>

namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */

#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define repf(i, l, r) for (int i = (l), i##_end_ = (r); i < i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#define curse(...) fprintf(stderr, __VA_ARGS__)

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
typedef vector<int> vset;

template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void chkmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void chkmax(T& x, const T rhs) { x = std::max(x, rhs); }

#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
    static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
    return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif

template<class T> inline T readret(T x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    return f ? -x : x;
}
template<class T> inline void readin(T& x) {;
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
    readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
    if (x < 0) putchar('-'), x = -x;
    static int __stk[55], __bit = 0;
    do __stk[++__bit] = x % 10, x /= 10; while (x);
    while (__bit) putchar(__stk[__bit--] ^ 48);
    putchar(c);
}

} // namespace Elaina
using namespace Elaina;

const int Maxn = 16;
const int n = 16;
const int Maxs = 1 << Maxn;

int dis[Maxs + 5];
priority_queue<pii, vector<pii>, greater<pii>> Q;
inline void dijkstra() {
    memset(dis, 0x3f, sizeof dis);
    Q.push({dis[0] = 0, 0});
    while (!Q.empty()) {
        int s = Q.top().se, d = Q.top().fi; Q.pop();
        if (d > dis[s]) continue;
        repf (i, 0, n) {
            rep (len, 0, i) {
                if (len < i && dis[s] > len) continue;
                int to = ((2 << len) - 1) << (i - len), w = max(dis[s], len) + 1;
                to ^= s;
                if (dis[to] > w) Q.push({dis[to] = w, to});
            }
        }
    }
}

signed main() {
    freopen("roll.in", "r", stdin);
    freopen("roll.out", "w", stdout);
    cin.tie(NULL)->sync_with_stdio(false);
    dijkstra();
    int s, t; char reci[Maxn + 5];
    cin >> t; while (t--) {
        cin >> reci;
        repf (i, s = 0, strlen(reci)) s = s << 1 | (reci[i] ^ 48);
        writln(dis[s]);
    }
    return 0;
}

落幕 / Fading

One minute I held the key

曾经我手握权位经脉


Next the walls were closed on me

如今才知宫墙深似海


And I discovered that my castles stand

恍然发现我的城池


Upon pillars of salt, pillars of sand

基底散如盐沙乱似尘埃

  可以发现一些性质,比如说有解的充要条件是 \(k\mid \sum a_i\),比如说如果 \([a,b]\) 有解,\([b+1,c]\) 有解,那么 \([l_1,r_2]\) 一定有解,并且若设 \(g(l,r)\) 表示区间 \([l,r]\) 的答案,那么 \(g(a,c)=g(a,b)+g(b+1,c)\),这告诉我们,对于一个右端点,我们实际上只需要计算与它最近的那个 \(l\) 且保证 \([l,r]\) 有解的 \(l\),而这样的区间是线性的,如果每一对我们可以快速地计算,那么最后的处理就非常容易了。

  分析到这里就有一个 \(\mathcal O(n^2)\) 的算法,每次枚举一个左端点,然后计算所有右端点的花费。

  不难发现上面的暴力的瓶颈在于,我们知道 \([l,r]\) 有解,但是计算 \(g(l,r)\) 花费了我们大量的时间,考虑快速地计算它:显然我们应当给每个需要的数定向,是向上还是向下凑成 \(k\) 的倍数,然后每个数就有 冗余售罄 两种情况,花费就是将 冗余 部分转移到 售罄 地方的总花费。

  十分容易可以证明,用 冗余 去补充 售罄,最小花费一定是先贪心地将最近的两个进行补充,然后再次近的......但是这样做显然不是特别好,问题有两个:一,如何将每个点定成 冗余 还是 售罄,二,即使可以确定,计算花费也不是特别方便。

  考虑从另外的方面进行计算,如果我们将前缀和看成条状,那么每一次修改就是将某个条拔高或者降低,并且其他的条不受到影响,而目标状态是让每个条的高度变成 \(k\) 的倍数,那么这个花费就很好计算了,记这个数列的前缀和对 \(k\) 取模之后为 \(pre_i\),那么总花费就是

\[\sum_{i=1}^{len}\min\set{pre_i,k-pre_i} \]

  记原序列的前缀和对 \(k\) 取模为 \(s_i\),那么,对于每一个右端点,找到最近的 \(l\) 使得 \(s_{l-1}=s_r\),计算 \([l,r]\) 的贡献,和上面相似,就是

\[\sum_{i=l}^r\min\set{s_i-s_{l-1},k-(s_i-s_{l-1})}\pmod{9998244353} \]

  注意 \(\min\) 里面的运算都要对 \(k\) 取模。这个式子的计算,实际上可以分成两个部分,记 \(x_i=s_i-s_{l-1}\),那么我们找到 \([l,r]\) 中有多少 \(x_i\in[0,\frac{k}{2}]\),这些直接取值,另外的就用 \(k\) 乘以个数减去和即可。实现可以使用主席树。

  代码细节有亿点多,但是应该是我写丑了。

/** @author __Elaina__ */

#pragma GCC optimize("Ofast")

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

#define USING_FREAD
// #define NDEBUG
#include <cassert>

namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */

#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define repf(i, l, r) for (int i = (l), i##_end_ = (r); i < i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#define masdf(...) fprintf(stderr, __VA_ARGS__)

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
typedef vector<int> vset;

template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void chkmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void chkmax(T& x, const T rhs) { x = std::max(x, rhs); }

#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
    static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
    return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif

template<class T> inline T readret(T x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    return f ? -x : x;
}
template<class T> inline void readin(T& x) {;
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
    readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
    if (x < 0) putchar('-'), x = -x;
    static int __stk[55], __bit = 0;
    do __stk[++__bit] = x % 10, x /= 10; while (x);
    while (__bit) putchar(__stk[__bit--] ^ 48);
    putchar(c);
}

} // namespace Elaina
using namespace Elaina;

const int Maxn = 1e6;
const int Maxk = 1e8;
const int logk = 30;
const int mod = 998244353;
inline void chkadd(int& x, int y) { if ((x += y) >= mod) x -= mod; }

int n, k;
int a[Maxn + 5];

inline void input() {
    readin(n, k);
    rep (i, 1, n) readin(a[i]);
}

namespace saya {
    
const int Maxn = ::Maxn * logk;

int ncnt;
int cnt[Maxn + 5], sum[Maxn + 5];
int ls[Maxn + 5], rs[Maxn + 5];

#define mid ((l + r) >> 1)
#define _lhs ls[i], l, mid
#define _rhs rs[i], mid + 1, r

inline int copy(int x) {
    int i = ++ncnt;
    tie(cnt[i], sum[i], ls[i], rs[i]) = tie(cnt[x], sum[x], ls[x], rs[x]);
    return i;
}

void modify(int p, int& i, int l = 0, int r = Maxk - 1) {
    i = copy(i), ++cnt[i], chkadd(sum[i], p);
    if (l == r) return ;
    if (p <= mid) modify(p, _lhs);
    else modify(p, _rhs);
}

inline void add(pii& x, pii y) { x.fi += y.fi, chkadd(x.se, y.se); }
pii query(int ql, int qr, int x, int y, int l = 0, int r = Maxk - 1) {
    if (ql > qr) return {0, 0};
    if (ql <= l && r <= qr) return {cnt[y] - cnt[x], (sum[y] + mod - sum[x]) % mod};
    pii ret = {0, 0};
    if (ql <= mid) ret = query(ql, qr, ls[x], ls[y], l, mid);
    if (mid < qr) add(ret, query(ql, qr, rs[x], rs[y], mid + 1, r));
    return ret;
}

#undef mid
#undef _lhs 
#undef _rhs 

};

int rt[Maxn + 5], s[Maxn + 5];
int reals[Maxn + 5];
inline void buildTree() {
    rep (i, 1, n) {
        s[i] = (s[i - 1] + a[i]) % k;
        reals[i] = (reals[i - 1] + s[i]) % mod;
        saya::modify(s[i], rt[i] = rt[i - 1]);
    }
}

int val[Maxn + 5];

inline int solve(int l, int r) {
    int mov = s[l - 1], les_cnt, gre_cnt, les_sum, gre_sum;
    if (mov + (k >> 1) < k) {
        int ql = mov, qr = mov + (k >> 1);
        auto ret = saya::query(ql, qr, rt[l - 1], rt[r]);
        tie(les_cnt, les_sum) = ret;
        gre_cnt = (r - l + 1) - les_cnt;
        gre_sum = ((0ll + reals[r] - reals[l - 1] - les_sum) % mod + mod) % mod;
        int number = saya::query(0, ql - 1, rt[l - 1], rt[r]).fi;
        les_sum = ((0ll + les_sum - 1ll * les_cnt * mov) % mod + mod) % mod;
        gre_sum = ((0ll + gre_sum + 1ll * k * number - 1ll * gre_cnt * mov) % mod + mod) % mod;
    }
    else {
        int ql = ((k >> 1) + 1 + mov) % k, qr = (k - 1 + mov) % k;
        auto ret = saya::query(ql, qr, rt[l - 1], rt[r]);
        tie(gre_cnt, gre_sum) = ret;
        les_cnt = (r - l + 1) - gre_cnt;
        les_sum = ((0ll + reals[r] - reals[l - 1] - gre_sum) % mod + mod) % mod;
        int number = saya::query(0, ql - 1, rt[l - 1], rt[r]).fi;
        les_sum = ((0ll + les_sum + 1ll * k * number - 1ll * les_cnt * mov) % mod + mod) % mod;
        gre_sum = ((0ll + gre_sum + 1ll * k * gre_cnt - 1ll * gre_cnt * mov) % mod + mod) % mod;
    }
    chkadd(les_sum, ((1ll * k * gre_cnt - gre_sum) % mod + mod) % mod);
    return les_sum;
}
map<int, int> pre;
int tol[Maxn + 5];
inline void prelude() {
    pre[0] = 0;
    rep (i, 1, n) {
        if (pre.count(s[i])) val[i] = solve(pre[s[i]] + 1, i), tol[i] = pre[s[i]];
        else val[i] = tol[i] = -1;
        pre[s[i]] = i;
    }
}

int ans = 0;
int f[Maxn + 5];
inline void getAns() {
    pre.clear(), pre[0] = 1;
    rep (i, 1, n) {
        chkadd(ans, 1ll * (mod - 1) * (i - pre[s[i]]) % mod);
        if (~tol[i]) {
            f[i] = 1ll * val[i] * pre[s[i]] % mod;
            chkadd(f[i], f[tol[i]]);
        }
        ++pre[s[i]];
        chkadd(ans, f[i]);
    }
    writln(ans);
}

signed main() {
    freopen("win.in", "r", stdin);
    freopen("win.out", "w", stdout);
    input();
    buildTree();
    prelude();
    getAns();
    return 0;
}

终焉 / Ending

Shattered windows and the sound of drums

断壁残垣礼崩乐坏


People couldn't believe what I'd become

世人不敢相信我已当年不再


Revolutionaries Wait

起义大军翘首期待


For my head on a silver plate

有朝一日我站上断头台

  转化之后将会变成一个很简单的问题,可是我并没有想到去转化......概率期望题还是多想想能不能转化再说。

  判断最优策略:在当前的手牌可以弑君再出牌。

  然后将赢的局数的期望转变一下:\(1+\mathsf{第一回合无法获胜的概率}+\mathsf{第二回合无法获胜的概率}+\mathsf{第三回合无法获胜的概率}\cdots\)

  注意到

\[1+\sum_{i=1}^{+\infty}\mathsf{第}\;i\;\mathsf{局无法获胜的概率}=1+\mathsf{拿到手里赢不了的抽牌序列的概率和} \]

  于是我们可以只关注抽排序列有哪些牌。

  1. 只有毒药:在 \(n\) 张牌以内都无法获胜,\(\displaystyle P_1=\sum_{i=1}^n\frac{1}{3^i}=\frac{1-\frac{1}{3^n}}{2}\)
  2. 只有火球,在 \(\displaystyle m=\ddiv{n-1}{2}\) 张牌以内无法获胜,\(\displaystyle P_2=\sum_{i=1}^m \frac{1}{3^i}=\frac{1-\frac{1}{3^m}}{2}\)
  3. 只有复读,无法获胜,\(\displaystyle P_3=\sum_{i=1}^{+\infty}\frac{1}{3^i}=\frac{1}{2}\)
  4. 毒药加火球,比较复杂,定义 \(f(0/1,0/1,s)\) 表示当前是否使用毒药,是否使用火球,造成 \(s\) 伤害的概率,最后的概率就是 \(\displaystyle P_4=\sum_{i=1}^{n-1}f(1,1,i)\)
  5. 毒药加复读,相当于毒药伤害为 \(1\),复读伤害为 \(2\),与上面的 DP 类似;
  6. 复读加上火球,其实和全是火球一样,但是要注意排除全是火球或者复读的情况,因此这一部分的概率为 \(\displaystyle P_6=\sum_{i=1}^m \frac{2^i-2}{3^i}=2\brak{1-\brak{\frac{2}{3}}^m}-\brak{1-\frac{1}{3^m}}\)
  7. 毒药加火球加复读,类似地,毒药为 \(1\),火球为 \(3\),复读为 \(4\) 就行了;

  注意到 \(4,5,7\) 的前缀和都是线性递推的形式,因此可以使用矩阵加速进行优化,可以把他们仨放到一个大矩阵中进行递推,这样比较方便,矩阵大小不超过 \(20\) 吧,因此复杂度大概就是 \(\mathcal O(T\times 20^3\log n)\).

  但是无码。

posted @ 2022-03-14 22:14  Arextre  阅读(54)  评论(0编辑  收藏  举报