20250812

T1

旅行家

显然每次走过两点间所有点双中的所有点。圆方树上树上差分打标记即可。

代码
#include <iostream>
#include <vector>
using namespace std;
#define getchar() p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++
char buf[1<<21], *p1, *p2, ch;
long long read() {
    long long ret = 0, neg = 0; char c = getchar(); neg = (c == '-');
    while (c < '0' || c > '9') c = getchar(), neg |= (c == '-');
    while (c >= '0' && c <= '9') ret = ret * 10 + c - '0', c = getchar();
    return ret * (neg ? -1 : 1);
}
int n, m, q;
int head[500005], nxt[4000005], to[4000005], ecnt;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int dfn[500005], low[500005], stk[500005], ssz, ncnt;
int ccnt;
int a[1000005];
vector<int> G[1000005];
void tarjan(int x) {
    dfn[x] = low[x] = ++ncnt;
    stk[++ssz] = x;
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (!dfn[v]) {
            tarjan(v);
            low[x] = min(low[x], low[v]);
            if (low[v] == dfn[x]) {
                ++ccnt;
                int t;
                do {
                    t = stk[ssz--];
                    G[ccnt].emplace_back(t);
                    G[t].emplace_back(ccnt);
                } while (t != v);
                G[ccnt].emplace_back(x);
                G[x].emplace_back(ccnt);
            }
        } else 
            low[x] = min(low[x], dfn[v]);
    }
}
int top[1000005], son[1000005], sz[1000005], dep[1000005], f[1000005];
int pre[1000005];
void dfs1(int x, int fa, int d) {
    dep[x] = d;
    f[x] = fa;
    sz[x] = 1;
    for (auto v : G[x]) {
        if (v != f[x]) {
            dfs1(v, x, d + 1);
            sz[x] += sz[v];
            if (sz[v] > sz[son[x]]) 
                son[x] = v;
        }
    }
}
void dfs2(int x, int t) {
    top[x] = t;
    if (son[x]) 
        dfs2(son[x], t);
    for (int v : G[x]) {
        if (v != f[x] && v != son[x]) 
            dfs2(v, v);
    }
}
int LCA(int x, int y) {
    while (top[x] ^ top[y]) (dep[top[x]] < dep[top[y]]) ? (y = f[top[y]]) : (x = f[top[x]]);
    return (dep[x] < dep[y] ? x : y);
}
void dfs3(int x) { for (int v : G[x]) if (v != f[x]) dfs3(v), pre[x] += pre[v]; }
bool vis[1000005];
int main() {
    n = read(), m = read();
    ccnt = n;
    for (int i = 1; i <= n; i++) a[i] = read();
    for (int i = 1; i <= m; i++) {
        int u = read(), v = read();
        add(u, v);
        add(v, u);
    }
    tarjan(1);
    dfs1(1, 0, 1);
    dfs2(1, 1);
    q = read();
    while (q--) {
        int x = read(), y = read(), z = LCA(x, y);
        pre[x]++, pre[y]++, pre[z]--, pre[f[z]]--;
    }
    dfs3(1);
    int ans = 0;
    for (int i = n + 1; i <= ccnt; i++) {
        if (pre[i] != 0) {
            for (auto v : G[i]) {
                ans += (!vis[v]) * a[v];
                vis[v] = 1;
            }
        }
    }
    cout << ans << "\n";
    return 0;
}

T2

铝装

先对原图建出边双连通森林,每次询问把所有点(包括新加边的端点和询问点集)处在的边双连通分量对应的点拿出来。然后把所有关键点按照其所在的连通块分组,每组内建虚树连边,再连上所有新边,然后把所有关键点和虚树边和新边一块跑一次 tarjan,然后判断是否所有询问点都在同一个边双当中即可。

代码
#include <iostream>
#include <algorithm>
#include <string.h>
#include <cassert>
#include <vector>
#define int long long
using namespace std;
int n, m, q;
int head[300005], nxt[600005], to[600005], ecnt = 1;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int dfn[500005], low[500005], stk[500005], ncnt, ssz;
vector<int> G[500005];
int bel[300005], Bel[300005], concnt;
int ccnt, Cr;
int rt[300005];
void tarjan(int x, int in) {
    dfn[x] = low[x] = ++ncnt;
    stk[++ssz] = x;
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (!dfn[v]) {
            tarjan(v, i);
            low[x] = min(low[x], low[v]);
        } else if (in ^ i ^ 1) 
            low[x] = min(low[x], dfn[v]);
    }
    if (low[x] == dfn[x]) {
        ++ccnt;
        int t;
        do {
            t = stk[ssz--];
            bel[t] = ccnt;
        } while (t != x);
    }
}
int top[1000005], son[1000005], sz[1000005], dep[1000005], f[1000005];
int pre[1000005];
void dfs1(int x, int fa, int d) {
    // cerr << x << " x\n";
    dep[x] = d;
    rt[x] = Cr;
    f[x] = fa;
    sz[x] = 1;
    for (auto v : G[x]) {
        if (v != f[x]) {
            dfs1(v, x, d + 1);
            sz[x] += sz[v];
            if (sz[v] > sz[son[x]]) 
                son[x] = v;
        }
    }
}
void dfs2(int x, int t) {
    top[x] = t;
    dfn[x] = ++ncnt;
    if (son[x]) 
        dfs2(son[x], t);
    for (int v : G[x]) {
        if (v != f[x] && v != son[x]) 
            dfs2(v, v);
    }
}
int LCA(int x, int y) {
    while (top[x] ^ top[y]) (dep[top[x]] < dep[top[y]]) ? (y = f[top[y]]) : (x = f[top[x]]);
    return (dep[x] < dep[y] ? x : y);
}
struct G {
    int head[300005], nxt[600005], to[600005], ecnt = 1;
    void add(int u, int v) {
        to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt;
        to[++ecnt] = u, nxt[ecnt] = head[v], head[v] = ecnt;
    }
    int dfn[500005], low[500005], stk[500005], ncnt, sz;
    int bel[300005], ccnt;
    void tarjan(int x, int in) {
        dfn[x] = low[x] = ++ncnt;
        stk[++sz] = x;
        for (int i = head[x]; i; i = nxt[i]) {
            int v = to[i];
            if (!dfn[v]) {
                tarjan(v, i);
                low[x] = min(low[x], low[v]);
            } else if (in ^ i ^ 1) 
                low[x] = min(low[x], dfn[v]);
        }
        if (low[x] == dfn[x]) {
            ++ccnt;
            int t;
            do {
                t = stk[sz--];
                bel[t] = ccnt;
            } while (t != x);
        }
    }
} G2;
void work(vector<int>& key, int r) {
    int asdf = 0;
    for (auto v : key) asdf |= (v == r);
    if (asdf != 1) key.emplace_back(r);
    sort(key.begin(), key.end(), [](int a, int b) { return dfn[a] < dfn[b]; });
    int kcnt = key.size();
    ssz = 0;
    stk[++ssz] = r;
    G2.dfn[r] = 0;
    G2.head[r] = 0;
    for (int i = 1; i < kcnt; i++) {
        G2.dfn[key[i]] = 0;
        G2.head[key[i]] = 0;
        assert(rt[stk[ssz]] == rt[key[i]]);
        int t = LCA(stk[ssz], key[i]);
        if (stk[ssz] != t) {
            while (ssz > 1 && dfn[t] < dfn[stk[ssz - 1]]) G2.add(stk[ssz - 1], stk[ssz]), --ssz;
            if (t != stk[ssz - 1]) {
                G2.dfn[t] = 0;
                G2.head[t] = 0;
                G2.add(t, stk[ssz]);
                stk[ssz] = t;
            } else {
                G2.add(t, stk[ssz]);
                --ssz;
            }
        }
        stk[++ssz] = key[i];
    }
    for (int i = ssz; i > 1; i--) G2.add(stk[i - 1], stk[i]);
}
vector<int> K;
int k1[300005], eu[300005], ev[300005];
bool in[300005];
signed main() {
    freopen("lianhua.in", "r", stdin);
    freopen("lianhua.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m >> q;
    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) 
            tarjan(i, 0);
    }
    for (int i = 1; i <= n; i++) {
        for (int v, j = head[i]; j; j = nxt[j]) {
            v = to[j];
            if (bel[i] != bel[v]) 
                G[bel[i]].emplace_back(bel[v]);
        }
    } ncnt = 0;
    memset(dfn, 0, sizeof dfn);
    for (int i = 1; i <= ccnt; i++) {
        if (!dfn[i]) {
            Cr = i;
            dfs1(i, 0, 1);
            dfs2(i, i);
        }
    }
    int lans = 0;
    for (int _ = 1; _ <= q; _++) {
        K.clear();
        int K1, K2;
        cin >> K1 >> K2;
        for (int i = 1; i <= K1; i++) {
            cin >> k1[i], k1[i] = (k1[i] + lans - 1) % n + 1;
            if (!in[bel[k1[i]]]) {
                K.emplace_back(bel[k1[i]]);
                in[bel[k1[i]]] = 1;
            }
        }
        for (int i = 1; i <= K2; i++) {
            int u, v;
            cin >> u >> v; u = (u + lans - 1) % n + 1, v = (v + lans - 1) % n + 1;
            eu[i] = u, ev[i] = v;
            if (!in[bel[u]]) 
                K.emplace_back(bel[u]);
            if (!in[bel[v]]) 
                K.emplace_back(bel[v]);
            in[bel[u]] = in[bel[v]] = 1;
        }
        sort(K.begin(), K.end(), [](int x, int y) { return rt[x] < rt[y]; });
        vector<int> tmp; G2.ecnt = 1;
        for (int i = 0; i < (int)K.size();) {
            int j = i;
            tmp.clear();
            while (j < (int)K.size() && rt[K[j]] == rt[K[i]]) tmp.emplace_back(K[j++]);
            work(tmp, rt[K[i]]);
            i = j;
        }
        for (int i = 1; i <= K2; i++) G2.add(bel[eu[i]], bel[ev[i]]);
        bool ok = 1;
        G2.sz = G2.ncnt = G2.ccnt = 0;
        for (int i = 1; i <= K1; i++) {
            if (!G2.dfn[bel[k1[i]]]) G2.tarjan(bel[k1[i]], 0);
            // cout << k1[i] << " " << G2.bel[k1[i]] << " y\n";
            ok &= (G2.bel[bel[k1[i]]] == G2.bel[bel[k1[1]]]);
        }
        if (ok) lans += _;
        cout << (ok ? "YES\n" : "NO\n");
        for (int v : K) in[v] = 0;
    }
    return 0;
}

T3

收缩的树

首先枚举一个点 \(rt\) 算答案。考虑树形 dp,由于我们只关心那些和 \(rt\) 相连的点,所以设 \(f_{u, j}\) 表示 \(u\) 被合并到 \(rt\) 所在的连通块前 \(u\) 子树内删了 \(j\) 条边的方案数。接下来我们要从儿子 \(v\)\(f_{v, k}\) 转移。中间隔了一条边 \((u, v)\),我们考虑这条边和 \(u\) 被合并到根的顺序。若 \((u, v)\) 先被删掉,那么我们并不关心它被删时到底带掉哪个点,只需要把它插进 \(v\) 子树里原来的 \(k\) 条边当中即可,由于首尾都可,所以方案数 \(k + 1\)。若 \(u\) 先被合并到根,那么 \((u, v)\) 被删掉的时候不能删 \(u\),因此带 \(\frac{1}{2}\) 的系数。对于第一种,由于 \(v\) 子树内和 \((u, v)\) 边都被算在 \(u\) 合并到根之前删去的边中,因此转移到 \(f_{u, j + k + 1}\)。对于第二种,由于此时 \(u\) 已经合并到根,因此 \(v\) 子树内和 \((u, v)\) 边可能不全算入 \(u\) 合并到根前删去的边中,因此会转移到 \(f_{u, j + x}, x \in [0, k]\)。然后合并删边序列需要再乘上两个组合数。实现时可以开辅助数组 \(g\) 辅助转移。最后答案即为 \(\frac{f_{rt, 0}}{(n - 1)!}\)。总复杂度 \(\mathcal{O}(n ^ 3)\)

代码
#include <iostream>
#include <string.h>
#define int long long
using namespace std;
const int P = 1000000007, i2 = (P + 1) / 2;
inline void Madd(int &x, int y) { (x += y) >= P ? (x -= P) : 0; }
int qpow(int x, int y = P - 2) {
    int ret = 1;
    while (y) {
        if (y & 1) 
            ret = ret * x % P;
        y >>= 1;
        x = x * x % P;
    }
    return ret;
}
int n, coe = 1;
int head[505], nxt[1005], to[1005], ecnt;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int f[505][505], sz[505];
int g[505][505], tmp[505];
int C[505][505];
void dfs(int x, int fa) {
    memset(f[x], 0, sizeof f[x]);
    f[x][0] = 1;
    sz[x] = 1;
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (v != fa) {
            dfs(v, x);
            memset(tmp, 0, sizeof tmp);
            for (int j = 0; j < sz[x]; j++) {
                for (int k = 0; k <= sz[v]; k++) 
                    Madd(tmp[j + k], f[x][j] * g[v][k] % P * C[j + k][j] % P * C[sz[x] - 1 - j + sz[v] - k][sz[v] - k] % P);
            }
            sz[x] += sz[v];
            for (int j = 0; j <= sz[x]; j++) f[x][j] = tmp[j];
        }
    }
    g[x][sz[x]] = 0;
    for (int i = sz[x] - 1; ~i; i--) g[x][i] = (g[x][i + 1] + f[x][i] * i2) % P;
    for (int i = sz[x]; i; i--) Madd(g[x][i], f[x][i - 1] * i % P);
}
signed main() {
    freopen("shtree.in", "r", stdin);
    freopen("shtree.out", "w", stdout);
    for (int i = C[0][0] = 1; i <= 500; i++) {
        for (int j = C[i][0] = 1; j <= i; j++) 
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;
    }
    cin >> n;
    for (int i = 1, u, v; i < n; i++) {
        coe = coe * qpow(i) % P;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    for (int i = 1; i <= n; i++) {
        dfs(i, 0);
        cout << f[i][0] * coe % P << " ";
    }
    cout << "\n";
    return 0;
}

T4

从天而降的区间

我们先翻转操作序列。接下来操作序列都被翻转了。

先考虑 sub 3。现在相当于我们已经有了一堆断点,然后每次给一个区间,需要求搞完了之后这个区间的期望长度。考虑我们拿这个区间倒着遍历操作序列,那么在撞断点的过程中,发现区间变化的过程就像在某种区间树上递归一样。那么我们考虑对所有断点按照时间建立大根笛卡尔树,那么这个时候我们再拿一个区间来询问,发现区间被崩坏的过程就像是在笛卡尔树上 dfs,每次以一半概率走左右儿子(或以 \(1\) 的概率只走左或右,取决于询问区间和笛卡尔树当前点的位置关系),走过去之后就把询问区间的 \(l\)\(r\) 分别和笛卡尔树的当前点取 \(\max\)\(\min\)。那么走到叶子之后我们就可以算答案了。

那么我们在笛卡尔树的每个点上维护其代表的区间的每个前缀和后缀的答案。不难发现维护了这个之后,只需要再对每个区间求出它第一次被崩坏是在哪个点 \(x\) 就可以轻易通过合并 \(lson_x\) 的后缀答案和 \(rson_x\) 的前缀答案算出询问的答案。接下来考虑维护前后缀答案。由于前后缀的对称性,下面只讨论前缀的维护。

我们发现左儿子的前缀答案可以直接拿过来当自己的(一部分)前缀答案,要考虑的只是右半部分。而对于右边部分,设左儿子的完整答案为 \(x\),则右边的所有前缀答案(设原本为 \(v\))要变成 \(\frac{v + x}{2}\)(容易理解)。这里就容易线段树了,但是复杂度有点高。我们考虑启发式合并

具体地,如果右儿子区间长度较小,我们直接枚举过去挨个改掉即可。如果左儿子区间长度较小,我们就给右儿子打一个整体加、乘标记。这个时候左儿子也会受影响,但由于其长度较小,我们直接枚举过去挨个做一遍对右儿子的标记的逆运算,这样两个儿子就可以共用一个标记。于是我们就完成了对前后缀答案的合并与维护。于是我们做完了 sub 3。

考虑原题。我们操作分块,设每 \(B\) 个分一段,对于每个询问,散块里的(也就是最后几个)崩坏操作我们直接暴力给它做掉,把每个区间分裂成 \(\mathcal{O}(B)\) 个区间。然后剩下的是前面的整块里的崩坏操作。那么这个时候我们发现我们所有的询问区间就都在修改之前了,那么我们套上 sub 3,就做完了。

这样的总复杂度是 \(\mathcal{O}(n \sqrt{n}\log n)\) 或者 \(\mathcal{O}(n \sqrt{n \log n})\)(视 \(n, q\) 同阶)的,视实现调块长可以做到。我不会实现,写的复杂度 \(\log\) 在根号外面。这是带根号的做法。

接下来会发现操作做完之后所有可能剩下的区间种类数只在 \(\mathcal{O}(q)\) 级别。因为没有被操作过的区间数量不超过 \(q\),两个端点中恰好一个是原端点的区间数量不超过 \(2q\),而两个端点都是划分点的区间,容易发现它们要么不交,要么包含。因此这种区间的总数也是 \(\mathcal{O}(q)\) 级别的。只有这三类区间了,因此最终的总区间数就在 \(\mathcal{O}(q)\) 级别。

于是我们有 \(\mathcal{O}(q \log q)\) 做法

首先最终区间只有本质不同的 \(\mathcal{O}(q)\) 种。接下来我们直接分治。与根号做法不同,这里我们不再翻转操作序列。我们考虑统计所有最终区间出现的期望次数,再(按长度)加权求和。

还是先考虑 sub 3。现在我们要求出每相邻两个修改之间的区间的期望出现次数(这里只考虑了最终两端点都被搞过的区间,显然剩下两种的贡献都是容易算的)。我们考虑对所有修改按时间建立小根笛卡尔树,然后考虑一个区间的加入会对哪些区间产生贡献。设这个区间第一次被分裂是在位置 \(x\),则所有受到贡献的区间如图:image.png,其中红色的是插入区间,绿色的是笛卡尔树,黑色的是笛卡尔树每个节点的对应区间,而所有蓝色点的子树内的区间都会受到贡献。然后这张图上只画了被 \(x\) 分裂后往右递归影响到的子树,被 \(x\) 分裂后往左递归的影响也是对称的。观察到贡献形如对 某一条链上的 位于询问区间内的点 的左子树产生系数为 \(\frac{1}{2 ^{其祖先中位于询问区间内点数量}}\) 的贡献。然后这条链一定延伸到叶子,因此我们考虑打一个 tag 到链底的这个叶子上,其值为该询问区间对这个叶子的贡献。然后从下往上合并标记,合并时对每个结点,我们把它右儿子上的 tag 打到左儿子上去(变成子树加标记),接下来合并左右儿子的 tag。对于右儿子的 tag,我们在打这个 tag 的时候(形式上是)进入了当前点的右儿子,也就是它被当前位置 \(x\) 分裂,因此右儿子的 tag 传上来的时候需要乘以 \(2\)。而左儿子的 tag 我们在打上它的时候并没有被当前位置 \(x\) 分裂,因此直接加上来。因此一个点的 tag 即为 \(2tag_r + tag_l\)。整个过程树上差分即可完成。总时间复杂度 \(\mathcal{O}(n)\)。这样我们就做完了 sub 3,总时间复杂度线性。

接下来搬到分治上,每个区间先递归左边,对右儿子中的所有修改建笛卡尔树,把左边搞出来的所有区间扔到右边笛卡尔树上,通过笛卡尔树在线性时间内求出最后搞出来的每个区间的期望出现次数。再加上线性 RMQ,总复杂度即可做到 \(\mathcal{O}(n \log n)\)。大概会有一些细节吧,反正我没写。但这个做法肯定是对的。

代码(根号重构+笛卡尔树,$\mathcal{O}(n \sqrt{n}\log n)$)
#include <iostream>
#include <algorithm>
#include <string.h>
#include <vector>
#include <array>
#include <set>
using namespace std;
#define getchar() p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++
char buf[1<<21], *p1, *p2, ch;
long long read() {
    long long ret = 0, neg = 0; char c = getchar(); neg = (c == '-');
    while (c < '0' || c > '9') c = getchar(), neg |= (c == '-');
    while (c >= '0' && c <= '9') ret = ret * 10 + c - '0', c = getchar();
    return ret * (neg ? -1 : 1);
}
const long long P = 998244353, i2 = (P + 1) >> 1;
const int B = 1000;
int qpow(int x, int y = P - 2) {
    int ret = 1;
    while (y) {
        if (y & 1) ret = 1ll * ret * x % P;
        y >>= 1, x = 1ll * x * x % P;
    }
    return ret;
}
inline void Madd(int &x, int y) { (x += y) >= P ? (x -= P) : 0; }
struct Tag {
    int k, b;
    Tag(int x = 1, int y = 0) { k = x, b = y; }
    int operator()(int x) { return (1ll * k * x + b) % P; }
    Tag operator~() { return Tag(qpow(k), P - 1ll * b * qpow(k) % P); }
} tgl[50005], tgr[50005];
Tag operator+(Tag a, Tag b) { return Tag(1ll * a.k * b.k % P, b(a.b)); }
int n, q, ans;
array<int, 3> op[50005];
vector<array<int, 3> > vec;
vector<pair<int, int> > vp[50005], vs[50005];
set<int> st;
int dep[50005], ipw[50005];
int mx[18][50005];
int a[50005];
inline int cmax(int x, int y) { return a[x] < a[y] ? y : x; }
inline int qmax(int l, int r) {
    int k = 63 - __builtin_clzll(r - l + 1);
    return cmax(mx[k][l], mx[k][r - (1 << k) + 1]);
}
inline void ins(int l, int r, int v) {
    int t;
    if (r <= l + 1 || a[t = qmax(l + 1, r - 1)] == 0) { Madd(ans, 1ll * (r - l) * v % P); return; }
    vp[t].emplace_back(r, v), vs[t].emplace_back(l, v);
}
int pre[50005], suf[50005], ncnt;
int Solve(int l, int r) {
    if (l + 1 >= r) return pre[r] = suf[l] = 1, pre[l] = suf[r] = 0;
    int mid = qmax(l + 1, r - 1), t, ls, rs, x = ++ncnt;
    if (!a[mid]) {
        for (int i = l; i <= r; i++) pre[i] = i - l, suf[i] = r - i;
        return x;
    }
    ls = Solve(l, mid), t = pre[mid], rs = Solve(mid, r), pre[mid] = t;
    for (auto v : vp[mid]) Madd(ans, i2 * tgl[rs](pre[v.first]) % P * v.second % P);
    for (auto v : vs[mid]) Madd(ans, i2 * tgr[ls](suf[v.first]) % P * v.second % P);
    if (mid - l < r - mid) {
        for (int i = l; i <= mid; i++) pre[i] = tgl[ls](pre[i]);
        tgl[x] = tgl[rs] + Tag(i2, pre[mid] * i2 % P); Tag tmp = ~tgl[x];
        for (int i = l; i <= mid; i++) pre[i] = tmp(pre[i]);
        tgr[x] = tgr[rs]; tmp = ~tgr[x]; t = tgr[rs](suf[mid]);
        for (int i = mid - 1; i >= l; i--) suf[i] = tmp((tgr[ls](suf[i]) + t) * i2 % P);
    } else {
        tgl[x] = tgl[ls]; Tag tmp = ~tgl[x]; t = tgl[ls](pre[mid]);
        for (int i = mid + 1; i <= r; i++) pre[i] = tmp((tgl[rs](pre[i]) + t) * i2 % P);
        for (int i = mid; i <= r; i++) suf[i] = tgr[rs](suf[i]);
        tgr[x] = tgr[ls] + Tag(i2, suf[mid] * i2 % P); tmp = ~tgr[x];
        for (int i = mid; i <= r; i++) suf[i] = tmp(suf[i]);
    }
    return x;
}
void work(int p) {
    for (int i = 0; i <= n; i++) a[i] = 0, mx[0][i] = i, vp[i].clear(), vs[i].clear(), tgl[i] = tgr[i] = Tag();
    for (int i = 1; i <= p; i++) if (op[i][0] == 2) a[op[i][1]] = i;
    for (int i = 1; (1 << i) <= n; i++) for (int j = 1; j + (1 << i) - 1 <= n; j++) mx[i][j] = cmax(mx[i - 1][j], mx[i - 1][j + (1 << (i - 1))]);
    for (auto v : vec) ins(v[0], v[1], v[2]);
    ncnt = 0; Solve(1, n);
}
signed main() {
    freopen("interval.in", "r", stdin);
    freopen("interval.out", "w", stdout);
    ipw[0] = 1;
    for (int i = 1; i <= 50000; i++) ipw[i] = ipw[i - 1] * i2 % P;
    n = read(), q = read();
    for (int i = 1; i <= q; i++) {
        op[i][0] = read();
        if (op[i][0] == 1) op[i][1] = read(), op[i][2] = read();
        else op[i][1] = read();
    }
    reverse(op + 1, op + q + 1);
    for (int i = 1, lst = 0; i <= q; i++) {
        if (op[i][0] == 1) {
            int l = op[i][1], r = op[i][2];
            st.clear(); vector<int> t;
            for (int j = i - 1; j > lst; j--) if (op[j][0] == 2 && l < op[j][1] && op[j][1] < r) t.emplace_back(op[j][1]);
            if (t.empty()) vec.push_back({ l, r, 1 });
            else {
                st.insert(0), st.insert(n + 1);
                dep[l] = dep[r] = 0;
                for (int v : t) {
                    if (st.count(v)) continue;
                    set<int>::iterator it = st.lower_bound(v);
                    int y = *it, x = *(--it);
                    dep[v] = max(dep[x], dep[y]) + 1;
                    st.insert(v);
                }
                t.emplace_back(l), t.emplace_back(r);
                sort(t.begin(), t.end());
                for (int j = 1; j < (int)t.size(); j++) vec.push_back({ t[j - 1], t[j], ipw[max(dep[t[j - 1]], dep[t[j]])] });
            }
        }
        if (i % B == 0 || i == q) work(lst), lst = i, vec.clear();
    }
    cout << ans << "\n";
    return 0;
}

T4,对叶子打标记,dfs 向上合并。

用限制定义状态。

posted @ 2025-08-14 23:02  forgotmyhandle  阅读(9)  评论(0)    收藏  举报