[题解]一氧化二氢

\[\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-02-12 一氧化二氢

  只能用 H2O 来形容这个比赛了......真就暴力都可以出不止一次奇迹了😅.

好 / Good

  天生对 DP 的畏惧使我并没有怎么分析这个题......被 不需要删完 给难到了......

  首先注意到,删除的是串,即使最后是中间删掉了一些再拼起来,实际上我们删掉的都是连续的一段,回过头来看 不一定要删完 其实就很好处理了 —— 只需要计算一下 \(dp(l,r)\) 表示区间 \([l,r]\) 删完的最大价值,最后再用一个一维 DP 就可以解决掉了。

  于是现在的问题来到了 \(dp(l,r)\),显然,类似一般的区间 DP,我们可以枚举一个断点,然后分成两个区间去分别删完。这种转移的特点是,\(l\)\(r\) 并不是在同一次删除中被删掉,那么显然还有一种转移 —— \(l,r\) 被同时删掉,此时我们需要枚举一个峰值 \(k\in [l,r]\) 使得 \(a_l\le a_k\land a_r\le a_k\),此时我们需要处理出两个东西,\(p(l,k)\)\(q(k,j)\),前者表示删完 \([l,k]\) 之后剩下一个从 \(a_l\)\(a_k\)递增序列的最大价值,后者表示删完 \([l,k]\) 之后剩下一个从 \(a_k\)\(a_j\)递减序列的最大价值,然后我们就可以进行两个转移:

\[dp(l,r)=\max\begin{cases} dp(l,k)+dp(k+1,r)&k\in [l,r) \\[2ex] p(l,k)+q(k,r)+v(a_k-a_l+a_k-a_r+1)&k\in [l,r) \end{cases} \]

  现在还有一个小问题:\(p(l,r)\)\(q(l,r)\) 怎么算?两者很相似,就说 \(p\) 了。枚举一个 \(k\) 使得 \(a_k=a_r-1\),那么 \(p(l,r)=p(l,k)+dp(k+1,r-1)\),即枚举递增数列的倒数第二项,就很容易转换为一个子问题。

  最后的复杂度是 \(\mathcal O(n^3)\) 的。

/** @author Arextre */

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

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

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

#define rep(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
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif

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

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 inf = 0x3f3f3f3f;
const int maxn = 400;

int n;
int a[maxn + 5], v[maxn + 5];
int dp[maxn + 5][maxn + 5];
int p[maxn + 5][maxn + 5], q[maxn + 5][maxn + 5];

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

inline void solve() {
    rep (i, 1, n) dp[i][i] = v[1];
    for (int len = 2; len <= n; ++len) {
        for (int l = 1; l <= n - len + 1; ++l) {
            int r = l + len - 1;
            dp[l][r] = p[l][r] = q[l][r] = -inf;
            if (a[l] < a[r] && a[r] - a[l] + 1 <= r - l + 1) {
                for (int k = l; k < r; ++k) if (a[k] == a[r] - 1)
                    chkmax(p[l][r], p[l][k] + dp[k + 1][r - 1]);
            }
            else if (a[l] > a[r] && a[l] - a[r] + 1 <= r - l + 1) {
                for (int k = l + 1; k <= r; ++k) if (a[k] == a[l] - 1)
                    chkmax(q[l][r], dp[l + 1][k - 1] + q[k][r]);
            }
            for (int k = l; k < r; ++k) chkmax(dp[l][r], dp[l][k] + dp[k + 1][r]);
            for (int k = l; k <= r; ++k) if (a[k] >= a[l] && a[k] >= a[r]) {
                if (a[k] - a[l] + a[k] - a[r] + 1 <= n)
                    chkmax(dp[l][r], p[l][k] + q[k][r] + v[a[k] - a[l] + a[k] - a[r] + 1]);
            }
        }
    }
}

int f[maxn + 5];
signed main() {
    freopen("good.in", "r", stdin);
    freopen("good.out", "w", stdout);
    input();
    solve();
    for (int i = 1; i <= n; ++i) {
        chkmax(f[i], f[i - 1]);
        for (int j = 0; j < i; ++j)
            chkmax(f[i], f[j] + dp[j + 1][i]);
    }
    writln(f[n]);
    return 0;
}

水 / Water

  原题目不是这个名字,但是这个题的数据是真的水,并且,如果将这道题和下一道题的题目改掉,就可以形容这场考试的 H2O 属性了。

  写了个 \(\mathcal O(qn\log n)\) 的算法还过了......甚至这个暴力都是暴力中最劣的......

  转换原题 —— 求所有 两个端点颜色不同的边 中的最小权值。

  就这个东西,不难想到 \(\mathcal O(q\sqrt{m\log n})\sim \mathcal O(q\sqrt m\log n)\) 的大小点分治。实现稍微好一点,最优下的复杂度将近 \(\mathcal O(5e8)\) 左右,在该题数据不水的情况下,可能会丢一部分的分。

  可能还存在更优的做法,此时应当分析性质,不难发现及证明,最终选择的边一定在 MST 上,因此,我们只需要在 MST 上维护上述东西就行了。实际上,我们可以比较暴力一点地维护这个东西,对于每个点开很多堆,记录其所有儿子中所有颜色集合的边权值,肯定用 vector/unordered_map 啊,不会有人开静态数组吧?,再开一个可删堆维护所有 两个端点颜色不同的边 的权值。修改的时候看一下父亲的堆,自己的堆,维护一下答案堆即可。最后的复杂度是 \(\mathcal O(n\log n+q\log n)\).

/** @author Arextre */

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

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

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

#define rep(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
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif

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

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 = 2e5;
const int maxm = 3e5;

int n, m, c, q;
int col[maxn + 5];

struct edge { int u, v, w; } Es[maxm + 5];
inline void input() {
    readin(n, m, c, q);
    int u, v, w;
    rep (i, 1, m) {
        readin(u, v, w);
        Es[i] = { u, v, w };
    }
    rep (i, 1, n) readin(col[i]);
}

namespace ufs {

int fa[maxn + 5];
inline void build() { rep (i, 1, n) fa[i] = i; }
inline int find(int u) {
    while (u ^ fa[u]) u = fa[u] = fa[fa[u]];
    return u;
}
inline bool merge(int u, int v) {
    u = find(u), v = find(v);
    return u == v? false: (fa[u] = v, true);
}

} // namespace ufs
vector<pii> g[maxn + 5];
inline void add_edge(int u, int v, int w) {
    g[u].push_back({ v, w });
    g[v].push_back({ u, w });
}
inline void buildTre() {
    static int tmp[maxm + 5];
    rep (i, 1, m) tmp[i] = i;
    sort(tmp + 1, tmp + m + 1, [](int i, int j) { return Es[i].w < Es[j].w; });
    ufs::build();
    rep (i, 1, m) if (ufs::merge(Es[tmp[i]].u, Es[tmp[i]].v))
        add_edge(Es[tmp[i]].u, Es[tmp[i]].v, Es[tmp[i]].w);
}

struct heap {
    priority_queue<int, vector<int>, greater<int>> rem, del;
    inline void push(int val) { rem.push(val); }
    inline void erase(int val) { del.push(val); }
    inline void move() {
        while (!del.empty() && rem.top() == del.top())
            rem.pop(), del.pop();
    }
    inline int size() { return move(), (int)(rem.size()) - (int)(del.size()); }
    inline int top() { return move(), rem.top(); }
} ans;
unordered_map<int, heap> buc[maxn + 5];
int fa[maxn + 5], tofa[maxn + 5];
void init(int u, int par) {
    for (const auto& [v, w]: g[u]) if (v ^ par) {
        buc[u][col[v]].push(w);
        fa[v] = u, tofa[v] = w;
        init(v, u);
    }
    for (auto& [c, H]: buc[u]) if (c ^ col[u])
        ans.push(H.top());
}

inline void modify(int u, int c) {
    if (col[u] == c) return ;
    if (fa[u]) { // not root
        heap& h = buc[fa[u]][col[u]];
        if (col[fa[u]] ^ col[u]) ans.erase(h.top());
        h.erase(tofa[u]);
        if (!h.size()) buc[fa[u]].erase(col[u]);
        else if (col[fa[u]] ^ col[u]) ans.push(h.top());

        heap& th = buc[fa[u]][c];
        if (th.size() && col[fa[u]] != c) ans.erase(th.top());
        th.push(tofa[u]);
        if (col[fa[u]] ^ c) ans.push(th.top());
    }
    if (buc[u].count(col[u])) ans.push(buc[u][col[u]].top());
    if (buc[u].count(c)) ans.erase(buc[u][c].top());
    col[u] = c;
}

signed main() {
    freopen("color.in", "r", stdin);
    freopen("color.out", "w", stdout);
    input();
    buildTre();
    init(1, 0);
    for (int x, y; q--; ) {
        readin(x, y);
        modify(x, y);
        if (!ans.size()) writln(0);
        else writln(ans.top());
    }
    return 0;
}

哟 / Ohh

  原题目不是这个名字,但是如果将这道题上一道题的题目改掉,就可以形容这场考试的 H2O 属性了。

  优化全部来自 \(\mathcal O(n^2)\) 的算法,而关键全在于 \(v_i\le v_{i+1}\) 这一条件。

  刚开始想用生成函数来解决这个问题,但是发现其对于 border 的刻画异常艰难,于是转战其他方向。

  如果没有 border 的限制,答案是 trivial 的,即 \(\displaystyle \prod_{i=1}^n v_i\),但是考虑上 border 的限制之后就没有那么显然了,先设计 \(f(i)\) 表示 \([1,i]\) 中,没有 border 的串的个数,接下来转移有两种方法:直接计算/容斥。

  直接计算好像有点难,因为一般的思路就是增量法,但是问题在于,可能一段长度小于 \(i\) 且存在 border 的串,在加上后面的一些东西之后没有 border 了,而 \(f\) 并没有包含这种东西,这使得转移较为困难。

  那么我们就要想容斥了,用 \(\displaystyle \prod_{i=1}^n v_i\) 减去存在 border 的情况,而存在 border 的非法方案的去重又有两个思路,枚举最小 border 长度和最大 border 长度,但是无论如何我们都要枚举一个 border 的长度 \(l\),且强制要求 \(s[1:l]=s[i-l+1:i]\),这个 要求要求\(s[i-l+1:i]\) 可以和 \(s[1:l]\) 一样,注意到 \(v_i\le v_{i+1}\),因此这个 要求 是可以被满足的,并且这提示我们思路是没有问题的,接下来就是选择枚举最长 border 和最短 border 两种选择。

  如果枚举最长 border 长度,似乎转移也没有什么用,但是如果枚举最短 border 长度,那么就要求了这个长度的 border 中不能还存在 border,这就归约到子问题,可以转移,因此,我们不难写出转移方程:

\[f(n)=P(n)-\sum_{i=1}^{\ddiv{n}{2}}f(i)P^{-1}(i)P(n-i) \]

  其中 \(\displaystyle P(n)=\prod_{i=1}^nv_i\).

  这是一个分治 NTT 的形式,唯一需要处理一下的就是上界 \(\ddiv{n}{2}\),这相当于 \(i\le j\),就是前一半卷后一半,稍微处理一下就行了,复杂度 \(\mathcal O(n\log^2 n)\),对于 \(n\le 10^6\) 很卡,但是可以做一个小优化,由于我们只需要计算 \(f(n)\),因此我们只需要算出 \(f(1)\sim f\brak{\ddiv{n}{2}}\) 的值即可,所以分治 NTT 的时候只需要算这个区间即可,这个剪枝减小了常数。

/** @author Arextre */

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

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

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

#define rep(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
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif

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

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 mod = 998244353;
inline void chkadd(int& x, int y) { if ((x += y) >= mod) x -= mod; }
inline int qkpow(int a, int n) {
    int ret = 1;
    for (; n; n >>= 1, a = 1ll * a * a % mod)
        if (n & 1) ret = 1ll * ret * a % mod;
    return ret;
}

namespace NTT {

int G[55];
inline void init() {
    for (int i = 1; i <= 50; ++i)
        G[i] = qkpow(3, (mod - 1) / (1 << i));
}
int rev[maxn * 10 + 5], n;
inline void prepare(int len) {
    for (n = 1; n < len; n <<= 1);
    for (int i = 0; i < n; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1)? (n >> 1) : 0);
}
inline void ntt(vector<int>& f, const int opt = 1) {
    f.resize(n);
    for (int i = 0; i < n; ++i) if (i < rev[i])
        swap(f[i], f[rev[i]]);
    for (int p = 2, lev = 1; p <= n; p <<= 1, ++lev) {
        int len = p >> 1, w = G[lev];
        for (int k = 0; k < n; k += p) {
            int buf = 1, tmp;
            for (int i = k; i < k + len; ++i, buf = 1ll * buf * w % mod) {
                tmp = 1ll * f[i + len] * buf % mod;
                f[i + len] = (f[i] + mod - tmp) % mod;
                chkadd(f[i], tmp);
            }
        }
    }
    if (opt != 1) {
        reverse(f.begin() + 1, f.end());
        int invn = qkpow(n, mod - 2);
        for (int i = 0; i < n; ++i) f[i] = 1ll * f[i] * invn % mod;
    }
}

} // namespace NTT

int n;
int prod[maxn + 5], invp[maxn + 5];
inline void input() {
    readin(n);
    prod[0] = 1;
    for (int i = 1; i <= n; ++i) {
        readin(prod[i]);
        prod[i] = 1ll * prod[i - 1] * prod[i] % mod;
        invp[i] = qkpow(prod[i], mod - 2);
    }
}

int dp[maxn + 5];
void solve(int l, int r) {
    if (l == r) {
        // printf("before, dp[%d] == %d\n", l, dp[l]);
        dp[l] = (prod[l] + mod - dp[l]) % mod;
        if (l << 1 <= n) chkadd(dp[l << 1], dp[l]);
        // printf("dp[%d] == %d\n", l, dp[l]);
        return ;
    }
    int mid = l + r >> 1;
    solve(l, mid);
    if (l + mid + 1 <= n) {
        // printf("Now l == %d, r == %d, mid == %d\n", l, r, mid);
        static vector<int> f, g;
        f.resize(mid - l + 1);
        g.resize(r - mid);
        for (int i = l; i <= mid; ++i) f[i - l] = 1ll * dp[i] * invp[i] % mod;
        for (int i = mid + 1; i <= r; ++i) g[i - mid - 1] = prod[i];
        // for (int x: f) writln(x, ' '); Endl;
        // for (int x: g) writln(x, ' '); Endl;
        NTT::prepare(r - l);
        NTT::ntt(f), NTT::ntt(g);
        for (int i = 0; i < NTT::n; ++i) f[i] = 1ll * f[i] * g[i] % mod;
        NTT::ntt(f, 1919810);
        // for (int x: f) writln(x, ' '); Endl;
        for (int i = 0; i < NTT::n; ++i) {
            if (i + l + mid + 1 > n) break;
            chkadd(dp[i + l + mid + 1], f[i]);
        }
    }
    solve(mid + 1, r);
}

signed main() {
    freopen("music.in", "r", stdin);
    freopen("music.out", "w", stdout);
    NTT::init();
    input();
    solve(1, n);
    writln(dp[n]);
    return 0;
}
posted @ 2022-03-14 21:57  Arextre  阅读(41)  评论(0编辑  收藏  举报