Exam Records 2

9.6

CSP 1

*B

link

题目描述

小诗有一个不可重集 \(S\) ,她记得 \(S\) 的元素数量 \(n\)\(\gcd⁡(S)+\operatorname{lcm}⁡(S)\) 的值 \(m\) ,但她已经忘记 \(S\) 由什么元素构成,她想知道有多少种符合条件的构成方案。由于答案过大,你只需告诉她答案 \(\bmod P\) 的值。

对于所有测试点,\(1\le n,m \le 10^9,10^8 \le P \le 10^9+7\),保证 \(P\) 为素数。

赛时读错题,读成 \(n\) 按位与 \(\gcd(S)+\operatorname{lcm}(S)\) 的值为 \(m\)

枚举 \(\gcd\) 设为 \(g\)\(g \mid m\)。可以计算出 \(\operatorname{lcm}\),设为 \(l\)

\(g,l\) 分解质因数,\(g=\sum p_i^{k_{1,i}}, l = \sum p_i^{k_{2,i}}\)。设对于集合中元素 \(a_x\) 分解质因数为 \(a_x\sum p_i^{t_{x,i}}\)。可知

\[\begin{aligned} k_{1,i} &= \min_{x=1}^{n} t_{x,i}\\ k_{2,i} &= \max_{x=1}^{n} t_{x,i} \end{aligned} \]

对于集合中的每个数分解质因子后,每个质因子都有出现次数的上下界。每个质因子出现次数的上下界都至少在一个数中出现过。

容斥解决这个问题。设 \(k_i = k_{2,i} - k_{1,i}\)。对于每个质因子枚举是否卡上下界,共 \(4^{w(l)}\) 种状态。对于上下界都没有限制的情况,其贡献为

\[{\prod(k_i+1) \choose n} \]

虽然 \(n\) 很大,但是 \(\prod(k_i+1)\) 很小,组合数可以计算。

\(c\) 为这个状态下为每个质因子上下界限制次数的和。例如钦定不能选择上界,则对 \(c\) 贡献 \(1\);钦定上下界都不能选,则对 \(c\) 贡献 \(2\)。容斥系数是 \((-1)^c\)

发现可以 \(3^{w(l)}\) 枚举状态,为限制了多少个上下界。设枚举的状态有 \(s\) 个质因子限制了 \(1\) 个上界或下界。则当前状态的贡献乘 \(2^s\)

代码
#include <cstdio>
#include <algorithm>

#define int long long

using namespace std;

const int N = 1000005;

int n, m, ans, mod, cnt, t[20], P[20], fac[N], inv[N], ifa[N];

int C(int x, int y)
{
    if(y > x)
        return 0;
    return fac[x] * ifa[y] % mod * ifa[x - y] % mod;
}

void solve(int x)
{
    cnt = 0;
    for(int i = 2; i * i <= x; ++ i)
    {
        if(x % i == 0)
            t[++ cnt] = 1;
        while(x % i == 0)
            x /= i, t[cnt] ++;
    }
    if(x != 1)
        t[++ cnt] = 2;

    int res = 0;
    for(int i = 0; i < P[cnt]; ++ i)
    {
        int a = 0, b = 0, c = 0, w = 1;
        for(int j = 1; j <= cnt; ++ j)
        {
            int o = i / P[j - 1] % 3;
            if(o == 0)
                ++ a, w *= t[j];
            else if(o == 1)
                ++ b, w *= t[j] - 1;
            else
                ++ c, w *= t[j] - 2;
        }
        if(b & 1)
            res = (res - C(w, n) * (1 << b) % mod + mod) % mod;
        else
            res = (res + C(w, n) * (1 << b)) % mod;
    }
    ans = (ans + res) % mod;
}

signed main()
{
    scanf("%lld%lld%lld", &n, &m, &mod);

    P[0] = fac[0] = inv[0] = inv[1] = ifa[0] = ifa[1] = 1;
    for(int i = 1; i <= 10; ++ i)
        P[i] = P[i - 1] * 3;
    for(int i = 1; i <= 1e6; ++ i)
        fac[i] = fac[i - 1] * i % mod;
    for(int i = 2; i <= 1e6; ++ i)
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    for(int i = 2; i <= 1e6; ++ i)
        ifa[i] = ifa[i - 1] * inv[i] % mod;

    for(int i = 1; i * i <= m; ++ i)
    {
        if(m % i)
            continue;
        int A = i, B = m - i;
        if(B >= A && B % A == 0)
            solve(B / A);
        if(i * i == m)
            continue;
        A = m / i, B = m - A;
        if(B >= A && B % A == 0)
            solve(B / A);
    }
    printf("%lld\n", ans);

    return 0;
}

可以使用莫反。

\(f(k)\) 表示 \(\gcd = 1,\operatorname{lcm} = k\)\(S\) 的个数。\(g(k)\) 表示 \(\gcd = 1,\operatorname{lcm} \mid k\)\(S\) 的个数。

根据转化可知,枚举 \(\gcd\) 后,答案为 \(f(\dfrac {m - g} g)\)

根据莫反得到:

\[\begin{aligned} g(k) &= \sum_{i \mid k} f(i)\\ f(k) &= \sum_{i \mid k} g(i) \mu(\frac k i) \end{aligned} \]

现在需要求 \(g\)

\[\begin{aligned} g(k) =& \sum_{\forall x \in S,x \mid k} [\gcd_{x \in S} x = 1]\\ =& \sum_{d \mid k} \mu(d) \sum_{\forall x \in S, x \mid k, d \mid x} 1\\ =& \sum_{d \mid k} \mu(d) {\sum_x[x \mid \frac k d] \choose n} \end{aligned} \]

\(h(k) = {\sum_x[x \mid k] \choose n} = {\sigma_0(k) \choose n}\),则 \(g = \mu * h,f = \mu * \mu * h\)\(\mu * \mu\) 是积性函数,可以直接计算。最后卷起来即可。

*C

link

题目描述

定义两个 01 串 \(S,T\) 满足 \(S\to T\) 当且仅当 \(S\) 中值为 \(1\) 的下标集合是 \(T\) 中值为 \(1\) 的下标集合的子集,定义 \(S\leftarrow T\) 当且仅当 \(T\to S\)

小诗有一个长为 \(n\) 的 01 串序列 \(s_1,s_2,\cdots,s_n\),其中每个 01 串长度为 \(m\),她想知道有多少子序列 \(1\le p_1<p_2<\cdots<p_k\le n\),使得存在 \(1\le t\le k\) 满足 \(s_{p_1}\to\cdots\to s_{p_t}\leftarrow \cdots\leftarrow s_{p_k}​​\)

由于答案过大,你只需告诉她答案 \(\bmod 10^9+7\) 的值。

\(1\le n \le 3\times 10^5,1\le m \le 20\)

上升下降分开考虑。

\(f_{i,j}\) 表示到枚举到的串时,前 \(10\) 位为 \(i\),后 \(10\) 位为 \(j\) 的子集的方案数。

注意最后合并的平台。

D

link

q-analog

9.7

CSP 2

*B

link

题目描述
void build(int i, int l, int r)
{
    L[i] = l;
    R[i] = r;
    if (l == r)
        return;
    int mid = (l + r) / 2;
    build(i * 2, l, mid);
    build(i * 2 + 1, mid + 1, r);
}

对于一棵有 \(n\) 个叶子的线段树,只需要执行 build(1,1,n) 就可以建出线段树。

对于给定的 \(n,x,y\),她想知道满足 \(x\le L_i\le R_i\le y\)\(i\) 的和是多少?

\(1 \le T \le 10^4\)\(1\le x \le y \le n \le 10^{18}\)

赛时读错题,读成节点个数。

先想普通线段树查询一样找到在 \(x,y\) 内,且不被其他在 \(x,y\) 内包含的节点。

\(f(x,p)\) 表示长度为 \(x\),标号为 \(p\) 的节点的答案。观察得到 \(f(x,p) = k(x) \times p + b(x)\)\(k,b\) 可以递推。

\[\begin{aligned} k(x) &= 2\times k(\lceil \frac x 2 \rceil) + 2 \times k(\lfloor \frac x 2 \rfloor) + 1\\ b(x) &= b(\lceil \frac x 2 \rceil) + b(\lfloor \frac x 2 \rfloor) + k(\lfloor \frac x 2 \rfloor) \end{aligned} \]

C

link

题目描述

菜汪酱是一名见习魔法师。施展一个魔法需要一根法杖和一个用于施展的咒语,每根法杖和每个咒语都各有两个参数 \(a,b\),施展魔法消耗的魔力定义为 法杖与咒语的 \(a\) 之和 以及 法杖与咒语的 \(b\) 之和 两者的较大值。

每次增加或删除一个咒语或法杖。询问需消耗的最小魔力。强制在线。

考虑咒语 \((a, b)\) 和法杖 \((u, v)\)。若 \(a + u > b + v\),则 \(a - b > v - u\)

建一棵线段树,维护区间咒语、法杖两个参数的最小值。咒语在 \(a - b\) 处更新,法杖在 \(v - u\) 处更新。每次用左区间 \(b\) 与右区间 \(v\) 的和,左区间 \(u\) 与右区间 \(a\) 的和更新答案。

注意删除操作。

*D

link

题目描述

菜汪酱在花园里除杂草。花园可以看作一根数轴,有 \(n\) 个位置有杂草,从左到右第 \(i\) 个位置和第 \(i+1\) 个位置间的距离是 \(Di\)​。初始时 \(n\) 个位置杂草的高度都是 \(0\),每一丛杂草每秒都会长高 \(1\) 厘米。菜汪酱每秒都能向左或右走一个单位长度,当菜汪酱到达一丛杂草的位置时,她可以立即将其拔掉,之后这丛杂草将不再会生长。菜汪酱 初始时在第 \(k\) 丛杂草的位置,她要将 \(n\) 丛杂草全部拔掉,请你帮她求出她最少需要拔掉总共多少厘米高的杂草?

\(s_i\)\(D_i\) 的前缀和。

假如有两个人割草,一个人向左走,一个人向右走,最终答案为 \(\sum_{i=1}^n\lvert s_k - s_i \rvert\)

对于第 \(i\) 棵草来说,最开始它的贡献是 \(\lvert s_k - s_i \rvert\)。每靠近它一步,它的贡献不变。每远离它一步,它的贡献 \(+ 2\)

\(dp_i\) 表示行走到 \(i\) 位置的答案。

\[\begin{aligned} dp_i + 2(i - 1)(s_j - s_i) \to dp_j\\ dp_j + 2(n - j)(s_j - s_i) \to dp_i \end{aligned} \]

其中 \(i \le k \le j\)

初始时 \(dp_k = \sum_{i=1}^n\lvert s_k - s_i \rvert\)。最终答案为 \(dp_1\)\(dp_n\)

使用类似 Dijkstra 的方式转移。

每次找到未被访问过的最小 \(dp_i\) 更新。对于 \(k \le i < j\),有 \(dp_i < dp_j\)。所以每次访问过的位置一定是包含 \(k\) 的一段区间 \([l, r]\)。每次访问过的位置能更新的最小 \(dp_i\) 的位置一定是 \(l - 1\)\(r + 1\)。因此每次比较 \(dp_{l - 1}\)\(dp_{r + 1}\) 即可。

可以单调队列进行斜率优化。

代码
#include <cstdio>
#include <algorithm>

#define int long long

using namespace std;

const int N = 2000005;

int n, K, s[N], f[N], q[2][N], head[2], tail[2];

int X(int i)
{
    return 2 * i;
}

int Y(int o, int i)
{
    if(o == 0)
        return f[i] - 2 * i * s[i] + 2 * s[i];
    else
        return f[i] + 2 * n * s[i] - 2 * i * s[i];
}

int W(int o, int i, int k)
{
    if(o == 0)
        return f[i] - 2 * i * s[i] + 2 * s[i] - k * 2 * i;
    else
        return f[i] + 2 * n * s[i] - 2 * i * s[i] - k * 2 * i;
}

double slope(int o, int i, int j)
{
    return 1.0 * (Y(o, i) - Y(o, j)) / (X(i) - X(j));
}

signed main()
{
    scanf("%lld%lld", &n, &K);

    s[1] = 1;
    for(int i = 2; i <= n; ++ i)
        scanf("%lld", &s[i]), s[i] += s[i - 1];

    for(int i = 1; i <= n; ++ i)
        f[K] += max(s[K], s[i]) - min(s[K], s[i]);

    q[0][1] = q[1][1] = K;
    head[0] = tail[0] = head[1] = tail[1] = 1;
    for(int l = K, r = K; l != 1 || r != n;)
    {
        if(r < n)
        {
            while(head[0] < tail[0] && W(0, q[0][head[0]], - s[r + 1]) > W(0, q[0][head[0] + 1], - s[r + 1]))
                ++ head[0];
            f[r + 1] = W(0, q[0][head[0]], - s[r + 1]) - 2 * s[r + 1];
        }
        if(l > 1)
        {
            while(head[1] < tail[1] && W(1, q[1][head[1]], - s[l - 1]) > W(1, q[1][head[1] + 1], - s[l - 1]))
                ++ head[1];
            f[l - 1] = W(1, q[1][head[1]], - s[l - 1]) - 2 * n * s[l - 1];
        }
        int x = l, y = r;
        if(y != n && (x == 1 || f[y + 1] <= f[x - 1]))
        {
            ++ r;
            while(head[1] < tail[1] && slope(1, r, q[1][tail[1]]) < slope(1, r, q[1][tail[1] - 1]))
                -- tail[1];
            q[1][++ tail[1]] = r;
        }
        if(x != 1 && (y == n || f[x - 1] <= f[y + 1]))
        {
            -- l;
            while(head[0] < tail[0] && slope(0, l, q[0][tail[0]]) > slope(0, l, q[0][tail[0] - 1]))
                -- tail[0];
            q[0][++ tail[0]] = l;
        }
    }
    printf("%lld\n", min(f[1], f[n]));

    return 0;
}

9.14

CSP+ 1

难度 B < A < C。

A

link

题目描述

在之后的第 \(i\) 个月中,他需要花费恰好 \(c_i\) 个钻石进行招募。

然而钻石并不是白给的,必须通过氪金才能获得。PCR 为玩家提供了 \(m\) 种不同的购买方案,每种购买方案用两个整数 \((a_i,b_i)\), 表示,表示这种方案中钻石的价格为 \(a_i\) 元一个,每个月最多用这种购买方案购买 \(b_i\)​ 个钻石。若 \(b_i=−1\),则表示这种购买方案没有次数限制。注意一个月中可以使用多种不同的购买方案进行购买,另外不同的月之间购买方案的种类并不会变。注意虽然第 \(i\) 个月需要花费恰好 \(c_i\) 个钻石,但是可以购买的量不受限制,可以购买超过 \(c_i\)​ 的钻石,留到之后的月用。

\(W\) 想要知道他需要在接下来的 \(n\) 个月中最少氪多少元才能够完成招募。

赛时想了对于每天考虑或者对于每个钻石考虑的思路。写了对于每个钻石考虑跑不满的暴力。

正难则反。对于每一天考虑。把钻石按照价格排序后,每天买的钻石是一个前缀。每天考虑把所有能买的钻石加入库存,加入库存不消耗代价。使用钻石时从库存中取出,此时消耗代价。这个过程可以使用线段树维护。

代码
#include <cstdio>
#include <algorithm>

#define ls p << 1
#define rs p << 1 | 1
#define int long long

using namespace std;

const int N = 200005;

int n, m, ans, c[N], pre[N], val[N];

struct Node
{
    int a, b;
};
Node a[N];

struct Tree
{
    int l, r, sum, res, k, b, B, cov;
};
Tree s[N << 2];

void build(int p, int l, int r)
{
    s[p].l = l;
    s[p].r = r;
    if(l == r)
        return;
    int mid = l + r >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
}

void tag(int p, int k, int b, int B)
{
    s[p].k += k;
    s[p].b += b;
    s[p].B += B;
    s[p].sum += k * (pre[s[p].r] - pre[s[p].l - 1]) + b;
    s[p].res += k * (val[s[p].r] - val[s[p].l - 1]) + B;
}

void push_down(int p)
{
    if(s[p].cov)
    {
        s[ls].sum = s[ls].res = s[ls].k = s[ls].b = s[ls].B = 0;
        s[rs].sum = s[rs].res = s[rs].k = s[rs].b = s[rs].B = 0;
        s[ls].cov = s[rs].cov = 1;
        s[p].cov = 0;
    }
    tag(ls, s[p].k, s[p].b, s[p].B);
    tag(rs, s[p].k, s[p].b, s[p].B);
    s[p].k = s[p].b = s[p].B = 0;
}

void query(int p, int k)
{
    if(s[p].l == s[p].r)
    {
        ans += k * a[s[p].l].a;
        s[p].sum -= k;
        s[p].res -= k * a[s[p].l].a;
        return;
    }
    push_down(p);
    int mid = s[p].l + s[p].r >> 1;
    if(s[ls].sum >= k)
        query(ls, k);
    else
    {
        ans += s[ls].res;
        query(rs, k - s[ls].sum);
        s[ls].cov = 1;
        s[ls].sum = s[ls].res = s[ls].k = s[ls].b = s[ls].B = 0;
    }
    s[p].sum = s[ls].sum + s[rs].sum;
    s[p].res = s[ls].res + s[rs].res;
}

signed main()
{
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);

    scanf("%lld%lld", &n, &m);
    for(int i = 1; i <= n; ++ i)
        scanf("%lld", &c[i]);
    for(int i = 1; i <= m; ++ i)
    {
        scanf("%lld%lld", &a[i].a, &a[i].b);
        if(a[i].b == -1)
            a[i].b = 1e6;
    }

    sort(a + 1, a + 1 + m, [](Node i, Node j)
        { return i.a < j.a; });

    for(int i = 1; i <= m; ++ i)
        pre[i] = pre[i - 1] + a[i].b, val[i] = val[i - 1] + a[i].a * a[i].b;

    build(1, 1, m);
    for(int i = 1; i <= n; ++ i)
    {
        s[1].k ++;
        s[1].sum += pre[m];
        s[1].res += val[m];
        query(1, c[i]);
    }

    printf("%lld\n", ans);

    return 0;
}

B

link

对于两个字符串 \(a, b\),定义 \(a \le b\)\(a + b\) 的字典序小于等于 \(b + a\) 的字典序。这是偏序关系。

*C

link

题目描述

\(n\) 堆石子,第 \(i\) 堆石子有 \(x_i\) 个,价值 \(y_i\)。小 \(W\) 选出一些石子堆的子集 \(S\),然后每个石子堆复制一份。现在有 \(2\lvert S \rvert\) 堆石子。对方选择任意堆数任意物品删除,不能全部删除。小 \(W\) 先手,轮流选择一堆或两堆石子,拿走任意颗石子(如果选择两堆,两堆拿走的石子可以不同)。不能操作者输。如果获胜,小 \(W\) 获得 \(\sum_{i \in S} y_i\) 的价值。否则,获得 \(0\) 的价值。

对于每个 \(i\),求仅使用前 \(i\) 堆石子的价值。

看清时对手随意删除一些石子堆。

如果有 \(n\) 堆石子,每次从一堆或两堆取石子,问先手胜还是后手胜。

对于 \(a_i\),转成二进制,做三进制不进位加法。先手必胜当且仅当答案不为 \(0\)

如果当前局面不为 \(0\),先手总能操作使得对手得到的局面为 \(0\)

把题目中石子大小的二进制称为向量 \(a_1,\cdots, a_n\)。如果后手必胜为存在不全是 \(0\)\(k_1,\cdots,k_n\) 三进制加法满足

\[k_1a_1+k_2a_2\cdots k_na_n= 0 \]

如果先手必胜,相当于 \(a_1,\cdots,a_n\) 在模 \(3\) 意义下线性无关。

使用线性基维护。注意每次插入新向量时,如果新向量的价值比线性基中当前位置的价值大,则更改当前位置的值。然后继续将原来线性基的值插入。

使用两个二进制数,一个维护当前位置三进制是否是 \(1\),一个维护三进制是否时 \(2\)

代码
#include <cstdio>
#include <algorithm>

#define int long long

using namespace std;

const int N = 500005;
const int K = (1ll << 61) - 1;

int n, ans, y[N], A[65], B[65], p[65], f[65], g[65];

void add(int &a, int &b, int x, int y)
{
    int u = a, v = b;
    a = (v & y) | (u & (K ^ (x | y))) | (x & (K ^ (u | v)));
    b = (u & x) | (v & (K ^ (x | y))) | (y & (K ^ (u | v))); 
}

void sub(int &a, int &b, int x, int y)
{
    int u = a, v = b;
    a = (v & x) | ((K ^ (u | v)) & y) | (u & (K ^ (x | y)));
    b = (u & y) | ((K ^ (u | v)) & x) | (v & (K ^ (x | y)));
}

void ins(int a, int b, int id)
{
    int u = 0, v = 0;
    for(int i = 60; i >= 0; -- i)
    {
        if(!(a >> i & 1) && !(b >> i & 1))
            continue;
        if(!p[i])
        {
            A[i] = a;
            B[i] = b;
            p[i] = id;
            ans += y[id];
            break;
        }
        if(y[p[i]] < y[id])
        {
            ans -= y[p[i]];
            ans += y[id];
            swap(p[i], id);
            swap(a, A[i]);
            swap(b, B[i]);
        }
        if(!p[i])
            continue;
        if(((a >> i & 1) && (A[i] >> i & 1)) || ((b >> i & 1) && (B[i] >> i & 1)))
            sub(a, b, A[i], B[i]);
        else
            add(a, b, A[i], B[i]);
    }
}

signed main()
{
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);

    scanf("%lld", &n);
    for(int i = 1, x, a, b; i <= n; ++ i)
    {
        scanf("%lld%lld", &x, &y[i]);
        ins(x ^ ans, 0, i);
        printf("%lld\n", ans);
    }

    return 0;
}

9.20

CSP+ 2

*B

link

题目描述

给定 \(n\) 个物品, 第 \(i\) 个物品有重量 \(w_i\)。这是一个特殊的世界,物品的质量可以是负数。

你将随意地取出这 \(n\) 个物品中的若干个,计算所有物品的重量然后搬走,正常的 oi 选手应该都知道这样的方案数有 \(2n\) 种(允许什么都不取和全部取)。

你很懒,你不想搬太大的重量,但是也不好意思什么都不搬,于是你想找到所有方案中总重量前 \(k\) 小的方案来供你选择。

首先设 \(sum = \sum_{a_i < 0} a_i\)。然后把 \(a_i < 0\) 的位置取反。

对于一个方案 \((val, num)\) 表示考虑到第 \(num\) 个元素,当前花费为 \(val\)。用堆维护可选方案。每次可以更新 \((val + a_{num + 1}, num + 1)\)\((val - a_{num} + a_{num + 1}, num + 1)\)。每次更新后的 \(val\) 一定不小于原 \(val\),且能生成 \(1 + 2 + 4 + \cdots + 2 ^{n - 1}\) 个方案。再加上最开始选所有负数的方案,可以做到不重不漏。

C

link

矩阵有一些点,询问矩形内有多少行有点。

三维偏序,可以树套树或 cdq。

可以莫队分块。

9.23

CSP 3

C

link

形式化题意:平面上有 \(n\) 个点,你要找到一个最小的 \(r\) 使得每个点为圆心画半径为 \(r\) 的圆,存在一条直线经过所有圆(相切也算经过)。

旋转卡壳,求两条线之间距离。

对于两条平行直线 \(Ax+By+C_1=0\)\(Ax+By+C_2 = 0\)

距离为

\[r = \frac {\lvert C_1 - C_2 \rvert} {\sqrt{A^2 + B^2}} \]

点线距

\[\frac {Ax + By + C}{\sqrt {A ^ 2 + B ^ 2}} \]

D

link

题目描述

定义

\[\operatorname{sgn}(x)=\left\{ \begin{aligned} &1,&x>0\\ &−1,&x<0\\ &0,&x=0 \end{aligned} \right. \]

有一个长度为 \(n\) 的数组 \(a_1,\cdots,a_n\),有 \(m\) 次操作,操作分为修改和询问。

修改分为四种:

  1. 给出 \(l,r,x\) 表示所有 \(l \le i \le r\)\(a_i\)​ 赋值为 \(x\)
  2. 给出 \(l,r,x\) 表示所有 \(l \le i \le r\)\(a_i\)​ 赋值成 \(\lvert a_i \rvert x\),其中 \(x=\pm 1\)
  3. 给出 \(l,r,x\) 表示所有 \(l \le i \le r\)\(a_i\)​ 赋值成 \(a_i\),其中 \(x=\pm 1\)
  4. 给出 \(l,r,x\) 表示所有 \(l \le i \le r\)\(a_i\)​ 赋值成 \(\operatorname{sgn}(a_i)x\)

查询的形式为给出 \(l,r\)。定义一个区间 \([L,R]\) 是合法的当且仅当 \(l \le L \le R \le r\) 并且 \(a_L>0,a_R>0\),定义一个区间 \([L,R]\) 是极长的当且仅当 \([L,R]\) 合法,\([L,R+1]\)\([L−1,R]\) 都不合法。定义一个极长区间的权值是所有区间内位置的权值和,定义一个位置的权值是所有能抵达的位置的 \(a_i\)​ 的积,如果 \(i\) 能抵达 \(j\) 当且仅当 \(l\le i,j\le r,\forall \min⁡(i,j)\le k\le \max⁡(i,j),a_k​>0\)。对于非正整数位置,权值为 \(0\)。你需要求出所有极长区间的权值和。对 \(2^{32}\) 取模。

保证值域为 \([−10^9,10^9] \setminus \{0\}\)

操作 \(1,2,3\) 直接用线段树维护,注意因为有取反操作,所以正号连续段和负号连续段各自分别维护。考虑操作 \(4\),长度为 \(len\) 的区间最多只有 \(\sqrt{len}\) 个本质不同的连续段。每个节点上维护长度为 \(x\) 的连续段的各种系数。每次修改是 \(\sqrt 1 + \sqrt 2 + \sqrt 4 + \cdots + \sqrt n\) 的。

注意 unordered_map 常数很大。我写的正解不如暴力跑的快。

代码

暴力做 \(4\) 操作。

#include <cstdio>
#include <algorithm>

#define ls p << 1
#define rs p << 1 | 1
#define int long long

using namespace std;

const int N = 100005;

int n, m, b[N];
unsigned a[N];

int Abs(int x)
{
    return x > 0 ? x : -x;
}

int Sgn(int x)
{
    return x > 0 ? 1 : (x < 0 ? -1 : 0);
}

unsigned Pow(unsigned x, int y)
{
    unsigned r = 1;
    for(; y; y >>= 1, x = x * x)
        if(y & 1)
            r = r * x;
    return r;
}

struct Tree
{
    int l, r, sgn, rev;
    unsigned cov, mul, num[2], sum[2], res[2], pre[2], suf[2], val[2][2], len[2][2];
};
Tree s[N << 2];

Tree operator + (Tree x, Tree y)
{
    Tree a;
    a.l = x.l;
    a.r = y.r;
    a.sgn = a.rev = a.cov = 0;
    a.mul = x.mul * y.mul;
    for(int o = 0; o < 2; ++ o)
    {
        if(x.len[o][1] && y.len[o][0])
        {
            a.num[o] = x.num[o] + y.num[o] - 1;
            a.sum[o] = x.sum[o] - x.val[o][1] * x.len[o][1] + y.sum[o] - y.val[o][0] * y.len[o][0] + x.val[o][1] * y.val[o][0] * (x.len[o][1] + y.len[o][0]);
            a.res[o] = x.res[o] - x.val[o][1] * x.len[o][1] * x.num[o] 
                        + y.res[o] - y.val[o][0] * y.len[o][0] * y.num[o] 
                        + (x.pre[o] - x.val[o][1] * x.len[o][1] * x.num[o]) * (y.num[o] - 1) 
                        + (y.suf[o] - y.val[o][0] * y.len[o][0] * y.num[o]) * (x.num[o] - 1)
                        + x.val[o][1] * y.val[o][0] * (x.len[o][1] + y.len[o][0]) * x.num[o] * y.num[o];
            a.pre[o] = x.pre[o] - x.val[o][1] * x.len[o][1] * x.num[o] 
                        + y.pre[o] - y.val[o][0] * y.len[o][0]
                        + (y.sum[o] - y.val[o][0] * y.len[o][0]) * (x.num[o] - 1) 
                        + x.val[o][1] * y.val[o][0] * (x.len[o][1] + y.len[o][0]) * x.num[o];
            a.suf[o] = y.suf[o] - y.val[o][0] * y.len[o][0] * y.num[o] 
                        + x.suf[o] - x.val[o][1] * x.len[o][1]
                        + (x.sum[o] - x.val[o][1] * x.len[o][1]) * (y.num[o] - 1)
                        + x.val[o][1] * y.val[o][0] * (x.len[o][1] + y.len[o][0]) * y.num[o];
            
            if(x.len[o][0] != x.r - x.l + 1)
                a.len[o][0] = x.len[o][0], a.val[o][0] = x.val[o][0];
            else
                a.len[o][0] = x.len[o][0] + y.len[o][0], a.val[o][0] = x.val[o][0] * y.val[o][0];
            
            if(y.len[o][1] != y.r - y.l + 1)
                a.len[o][1] = y.len[o][1], a.val[o][1] = y.val[o][1];
            else
                a.len[o][1] = y.len[o][1] + x.len[o][1], a.val[o][1] = x.val[o][1] * y.val[o][1]; 

            continue;
        }
        a.num[o] = x.num[o] + y.num[o];
        a.sum[o] = x.sum[o] + y.sum[o];
        a.res[o] = x.res[o] + y.res[o] + x.pre[o] * y.num[o] + x.num[o] * y.suf[o];
        a.pre[o] = x.pre[o] + y.pre[o] + y.sum[o] * x.num[o];
        a.suf[o] = y.suf[o] + x.suf[o] + x.sum[o] * y.num[o];
        a.val[o][0] = x.val[o][0];
        a.val[o][1] = y.val[o][1];
        a.len[o][0] = x.len[o][0];
        a.len[o][1] = y.len[o][1];
    }
    return a;
}

void Rev(int p)
{
    s[p].rev ^= 1;
    s[p].sgn = - s[p].sgn;
    swap(s[p].num[0], s[p].num[1]);
    swap(s[p].sum[0], s[p].sum[1]);
    swap(s[p].res[0], s[p].res[1]);
    swap(s[p].pre[0], s[p].pre[1]);
    swap(s[p].suf[0], s[p].suf[1]);
    swap(s[p].val[0][0], s[p].val[1][0]);
    swap(s[p].val[0][1], s[p].val[1][1]);
    swap(s[p].len[0][0], s[p].len[1][0]);
    swap(s[p].len[0][1], s[p].len[1][1]);
}

void Cov(int p, int a, int b)
{
    s[p].cov = a;
    s[p].sgn = b;
    int o = max(b, 0ll);
    s[p].num[o] = 1;
    s[p].len[o][0] = s[p].len[o][1] = s[p].r - s[p].l + 1;
    s[p].mul = s[p].val[o][0] = s[p].val[o][1] = Pow(a, s[p].r - s[p].l + 1);
    s[p].sum[o] = s[p].res[o] = s[p].pre[o] = s[p].suf[o] = s[p].mul * (s[p].r - s[p].l + 1);
    o = !o;
    s[p].num[o] = s[p].sum[o] = s[p].res[o] = s[p].pre[o] = s[p].suf[o] = s[p].val[o][0] = s[p].val[o][1] = s[p].len[o][0] = s[p].len[o][1] = 0;
}

void Sign(int p, int x)
{
    s[p].sgn = x;
    int o = max(x, 0ll);
    s[p].num[o] = 1;
    s[p].len[o][0] = s[p].len[o][1] = s[p].r - s[p].l + 1;
    s[p].val[o][0] = s[p].val[o][1] = s[p].mul;
    s[p].sum[o] = s[p].res[o] = s[p].pre[o] = s[p].suf[o] = s[p].mul * (s[p].r - s[p].l + 1);
    o = !o;
    s[p].num[o] = s[p].sum[o] = s[p].res[o] = s[p].pre[o] = s[p].suf[o] = s[p].val[o][0] = s[p].val[o][1] = s[p].len[o][0] = s[p].len[o][1] = 0;
}

void push_down(int p)
{
    if(s[p].rev)
    {
        Rev(ls);
        Rev(rs);
        s[p].rev = 0;
    }
    if(s[p].cov)
    {
        Cov(ls, s[p].cov, s[p].sgn);
        Cov(rs, s[p].cov, s[p].sgn);
        s[p].cov = s[p].sgn = 0;
    }
    if(s[p].sgn)
    {
        Sign(ls, s[p].sgn);
        Sign(rs, s[p].sgn);
        s[p].sgn = 0;
    }
}

void build(int p, int l, int r)
{
    s[p].l = l;
    s[p].r = r;
    if(l == r)
    {
        if(b[l] != 0)
        {
            int o = max(b[l], 0ll);
            s[p].num[o] = s[p].len[o][0] = s[p].len[o][1] = 1;
            s[p].sum[o] = s[p].res[o] = s[p].pre[o] = s[p].suf[o] = s[p].val[o][0] = s[p].val[o][1] = s[p].mul = a[l];
        }
        return;
    }
    int mid = l + r >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    s[p] = s[ls] + s[rs];
}

void updCov(int p, int l, int r, int x, int y)
{
    if(s[p].l >= l && s[p].r <= r)
        return Cov(p, x, y);
    push_down(p);
    int mid = s[p].l + s[p].r >> 1;
    if(l <= mid)
        updCov(ls, l, r, x, y);
    if(r > mid)
        updCov(rs, l, r, x, y);
    s[p] = s[ls] + s[rs];
}

void updSgn(int p, int l, int r, int x)
{
    if(s[p].l >= l && s[p].r <= r)
        return Sign(p, x);
    push_down(p);
    int mid = s[p].l + s[p].r >> 1;
    if(l <= mid)    
        updSgn(ls, l, r, x);
    if(r > mid)
        updSgn(rs, l, r, x);
    s[p] = s[ls] + s[rs];
}

void updRev(int p, int l, int r)
{
    if(s[p].l >= l && s[p].r <= r)
        return Rev(p);
    push_down(p);
    int mid = s[p].l + s[p].r >> 1;
    if(l <= mid)    
        updRev(ls, l, r);
    if(r > mid)
        updRev(rs, l, r);
    s[p] = s[ls] + s[rs];
}   

void updVal(int p, int l, int r, int x, int y)
{
    if(s[p].l >= l && s[p].r <= r)
    {
        if(s[p].len[0][0] == s[p].r - s[p].l + 1)
            return Cov(p, x, - y);
        if(s[p].len[1][0] == s[p].r - s[p].l + 1)
            return Cov(p, x, y);
    }
    push_down(p);
    int mid = s[p].l + s[p].r >> 1;
    if(l <= mid)
        updVal(ls, l, r, x, y);
    if(r > mid)
        updVal(rs, l, r, x, y);
    s[p] = s[ls] + s[rs];
}

Tree get(int p, int l, int r)
{
    if(s[p].l >= l && s[p].r <= r)
        return s[p];
    push_down(p);
    int mid = s[p].l + s[p].r >> 1;
    if(r <= mid)
        return get(ls, l, r);
    if(l > mid)
        return get(rs, l, r);
    return get(ls, l, r) + get(rs, l, r);
}

signed main()
{
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);

    scanf("%lld%lld", &n, &m);
    for(int i = 1, x; i <= n; ++ i)
        scanf("%lld", &x), a[i] = Abs(x), b[i] = Sgn(x);

    build(1, 1, n);

    for(int i = 1, o, l, r, x; i <= m; ++ i)
    {
        scanf("%lld%lld%lld", &o, &l, &r);
        if(o != 5)
            scanf("%lld", &x);
        if(o == 1)
            updCov(1, l, r, Abs(x), Sgn(x));
        if(o == 2)
            updSgn(1, l, r, Sgn(x));
        if(o == 3 && x < 0)
            updRev(1, l, r);
        if(o == 4)
            updVal(1, l, r, Abs(x), Sgn(x));
        if(o == 5)
            printf("%u\n", get(1, l, r).res[1]);
        o = l + r;
    }

    return 0;
}

过不去的正解。

#include <cstdio>
#include <vector>
#include <algorithm>
#include <unordered_map>

#define ls p << 1
#define rs p << 1 | 1

using namespace std;

const int N = 100005;

int n, m, b[N];
unsigned a[N];

unsigned I[131][100001], J[131][100001];

inline int Abs(int x)
{
    return x > 0 ? x : -x;
}

inline int Sgn(int x)
{
    return x > 0 ? 1 : (x < 0 ? -1 : 0);
}

unsigned Pow(unsigned x, int y)
{
    if(y < 16900)
        return I[y % 130][x] * J[y / 130][x];
    unsigned r = 1;
    for(x = I[1][x]; y; y >>= 1, x = x * x)
        if(y & 1)
            r = r * x;
    return r;
}

struct Tree
{
    int l, r, sgn, rev;
    unsigned cov, mul, num[2], sum[2], res[2], pre[2], suf[2], val[2][2], len[2][2];
    vector < pair<int, unsigned> > T[2], L[2], R[2], C[2];
};
Tree s[N << 2];

unsigned U[N], V[N], H[N];

Tree operator + (Tree x, Tree y)
{
    Tree a;
    a.l = x.l;
    a.r = y.r;
    a.sgn = a.rev = a.cov = 0;
    a.mul = x.mul * y.mul;
    for(int o = 0; o < 2; ++ o)
    {
        if(x.len[o][1] && y.len[o][0])
        {
            unsigned X = x.len[o][1], Y = y.len[o][0];
            unsigned P = x.val[o][1], Q = y.val[o][0];
            unsigned XW = x.pre[o] - P * x.len[o][1] * x.num[o], YW = y.suf[o] - Q * y.len[o][0] * y.num[o];
            a.num[o] = x.num[o] + y.num[o] - 1;
            a.sum[o] = x.sum[o] - P * X + y.sum[o] - Q * Y + P * Q * (X + Y);
            a.res[o] = x.res[o] - P * X * x.num[o] 
                        + y.res[o] - Q * Y * y.num[o] 
                        + XW * (y.num[o] - 1) 
                        + YW * (x.num[o] - 1)
                        + P * Q * (X + Y) * x.num[o] * y.num[o];
            a.pre[o] = XW 
                        + y.pre[o] - Q * Y
                        + (y.sum[o] - Q * Y) * (x.num[o] - 1) 
                        + P * Q * (X + Y) * x.num[o];
            a.suf[o] = YW 
                        + x.suf[o] - P * X
                        + (x.sum[o] - P * X) * (y.num[o] - 1)
                        + P * Q * (X + Y) * y.num[o];
            
            if(x.len[o][0] != x.r - x.l + 1)
                a.len[o][0] = x.len[o][0], a.val[o][0] = x.val[o][0];
            else
                a.len[o][0] = x.len[o][0] + Y, a.val[o][0] = x.val[o][0] * Q;
            
            if(y.len[o][1] != y.r - y.l + 1)
                a.len[o][1] = y.len[o][1], a.val[o][1] = y.val[o][1];
            else
                a.len[o][1] = y.len[o][1] + X, a.val[o][1] = P * y.val[o][1]; 

            for(auto u : x.L[o])
                U[u.first] = u.second;
            for(auto u : y.R[o])
                V[u.first] = u.second;

            for(auto u : x.T[o])
                H[u.first] += u.second + U[u.first] * (y.num[o] - 1);
            for(auto u : y.T[o])
                H[u.first] += u.second + V[u.first] * (x.num[o] - 1);
            H[X] -= x.num[o] * y.num[o];
            H[Y] -= x.num[o] * y.num[o];
            H[X + Y] += x.num[o] * y.num[o];
            
            for(auto u : x.T[o])
                if(H[u.first])
                    a.T[o].push_back(make_pair(u.first, H[u.first])), H[u.first] = 0;
            for(auto u : y.T[o])
                if(H[u.first])
                    a.T[o].push_back(make_pair(u.first, H[u.first])), H[u.first] = 0;
            if(H[X + Y])
                a.T[o].push_back(make_pair(X + Y, H[X + Y])), H[X + Y] = 0;
            
            for(auto u : y.C[o])
                U[u.first] = u.second;
            for(auto u : x.L[o])
                H[u.first] += u.second;
            for(auto u : y.L[o])
                H[u.first] += u.second + (x.num[o] - 1) * U[u.first];
            H[X] -= x.num[o];
            H[Y] -= x.num[o];
            H[X + Y] += x.num[o];

            for(auto u : x.L[o])
                if(H[u.first])
                    a.L[o].push_back(make_pair(u.first, H[u.first])), H[u.first] = 0;
            for(auto u : y.L[o])
                if(H[u.first])
                    a.L[o].push_back(make_pair(u.first, H[u.first])), H[u.first] = 0;
            if(H[X + Y])
                a.L[o].push_back(make_pair(X + Y, H[X + Y])), H[X + Y] = 0;

            for(auto u : x.C[o])
                U[u.first] = u.second;
            for(auto u : x.R[o])
                H[u.first] += u.second + (y.num[o] - 1) * U[u.first];
            for(auto u : y.R[o])
                H[u.first] += u.second;
            H[X] -= y.num[o];
            H[Y] -= y.num[o];
            H[X + Y] += y.num[o];

            for(auto u : x.R[o])
                if(H[u.first])
                    a.R[o].push_back(make_pair(u.first, H[u.first])), H[u.first] = 0;
            for(auto u : y.R[o])
                if(H[u.first])
                    a.R[o].push_back(make_pair(u.first, H[u.first])), H[u.first] = 0;
            if(H[X + Y])
                a.R[o].push_back(make_pair(X + Y, H[X + Y])), H[X + Y] = 0;

            for(auto u : x.C[o])
                H[u.first] += u.second;
            for(auto u : y.C[o])
                H[u.first] += u.second;
            -- H[X];
            -- H[Y];
            H[X + Y] ++;

            for(auto u : x.C[o])
                if(H[u.first])
                    a.C[o].push_back(make_pair(u.first, H[u.first])), H[u.first] = 0;
            for(auto u : y.C[o])
                if(H[u.first])
                    a.C[o].push_back(make_pair(u.first, H[u.first])), H[u.first] = 0;
            if(H[X + Y])
                a.C[o].push_back(make_pair(X + Y, H[X + Y])), H[X + Y] = 0;

            continue;
        }
        a.num[o] = x.num[o] + y.num[o];
        a.sum[o] = x.sum[o] + y.sum[o];
        a.res[o] = x.res[o] + y.res[o] + x.pre[o] * y.num[o] + x.num[o] * y.suf[o];
        a.pre[o] = x.pre[o] + y.pre[o] + y.sum[o] * x.num[o];
        a.suf[o] = y.suf[o] + x.suf[o] + x.sum[o] * y.num[o];
        a.val[o][0] = x.val[o][0];
        a.val[o][1] = y.val[o][1];
        a.len[o][0] = x.len[o][0];
        a.len[o][1] = y.len[o][1];

        for(auto u : x.L[o])
            U[u.first] = u.second;
        for(auto u : y.R[o])
            V[u.first] = u.second;

        for(auto u : x.T[o])
            H[u.first] += u.second + U[u.first] * y.num[o];
        for(auto u : y.T[o])
            H[u.first] += u.second + x.num[o] * V[u.first];

        for(auto u : x.T[o])
            if(H[u.first])
                a.T[o].push_back({u.first, H[u.first]}), H[u.first] = 0;
        for(auto u : y.T[o])
            if(H[u.first])
                a.T[o].push_back({u.first, H[u.first]}), H[u.first] = 0;

        for(auto u : y.C[o])
            U[u.first] = u.second;

        for(auto u : x.L[o])
            H[u.first] += u.second;
        for(auto u : y.L[o])
            H[u.first] += u.second + x.num[o] * U[u.first];

        for(auto u : x.L[o])
            if(H[u.first])
                a.L[o].push_back({u.first, H[u.first]}), H[u.first] = 0;
        for(auto u : y.L[o])
            if(H[u.first])
                a.L[o].push_back({u.first, H[u.first]}), H[u.first] = 0;

        for(auto u : x.C[o])
            U[u.first] = u.second;

        for(auto u : x.R[o])
            H[u.first] += u.second + U[u.first] * y.num[o];
        for(auto u : y.R[o])
            H[u.first] += u.second;

        for(auto u : x.R[o])
            if(H[u.first])
                a.R[o].push_back({u.first, H[u.first]}), H[u.first] = 0;
        for(auto u : y.R[o])
            if(H[u.first])
                a.R[o].push_back({u.first, H[u.first]}), H[u.first] = 0;

        for(auto u : x.C[o])
            H[u.first] += u.second;
        for(auto u : y.C[o])
            H[u.first] += u.second;

        for(auto u : x.C[o])
            if(H[u.first])
                a.C[o].push_back({u.first, H[u.first]}), H[u.first] = 0;
        for(auto u : y.C[o])
            if(H[u.first])
                a.C[o].push_back({u.first, H[u.first]}), H[u.first] = 0;
    }
    return a;
}

inline void Rev(int p)
{
    s[p].rev ^= 1;
    s[p].sgn = - s[p].sgn;
    swap(s[p].num[0], s[p].num[1]);
    swap(s[p].sum[0], s[p].sum[1]);
    swap(s[p].res[0], s[p].res[1]);
    swap(s[p].pre[0], s[p].pre[1]);
    swap(s[p].suf[0], s[p].suf[1]);
    swap(s[p].val[0][0], s[p].val[1][0]);
    swap(s[p].val[0][1], s[p].val[1][1]);
    swap(s[p].len[0][0], s[p].len[1][0]);
    swap(s[p].len[0][1], s[p].len[1][1]);
    swap(s[p].T[0], s[p].T[1]);
    swap(s[p].L[0], s[p].L[1]);
    swap(s[p].R[0], s[p].R[1]);
    swap(s[p].C[0], s[p].C[1]);
}

inline void Cov(int p, unsigned a, int b)
{
    s[p].T[0].clear(), s[p].T[1].clear();
    s[p].L[0].clear(), s[p].L[1].clear();
    s[p].R[0].clear(), s[p].R[1].clear();
    s[p].C[0].clear(), s[p].C[1].clear();

    s[p].cov = a;
    s[p].sgn = b;
    int o = max(b, 0);
    s[p].num[o] = 1;
    s[p].len[o][0] = s[p].len[o][1] = s[p].r - s[p].l + 1;
    s[p].mul = s[p].val[o][0] = s[p].val[o][1] = Pow(a, s[p].r - s[p].l + 1);
    s[p].sum[o] = s[p].res[o] = s[p].pre[o] = s[p].suf[o] = s[p].mul * (s[p].r - s[p].l + 1);

    pair <int, unsigned> w = make_pair(s[p].r - s[p].l + 1, 1);
    s[p].T[o].push_back(w);
    s[p].L[o].push_back(w);
    s[p].R[o].push_back(w);
    s[p].C[o].push_back(w);

    o = !o;
    s[p].num[o] = s[p].sum[o] = s[p].res[o] = s[p].pre[o] = s[p].suf[o] = s[p].val[o][0] = s[p].val[o][1] = s[p].len[o][0] = s[p].len[o][1] = 0;
}

inline void Sign(int p, int x)
{
    s[p].T[0].clear(), s[p].T[1].clear();
    s[p].L[0].clear(), s[p].L[1].clear();
    s[p].R[0].clear(), s[p].R[1].clear();
    s[p].C[0].clear(), s[p].C[1].clear();

    s[p].sgn = x;
    int o = max(x, 0);
    s[p].num[o] = 1;
    s[p].len[o][0] = s[p].len[o][1] = s[p].r - s[p].l + 1;
    s[p].val[o][0] = s[p].val[o][1] = s[p].mul;
    s[p].sum[o] = s[p].res[o] = s[p].pre[o] = s[p].suf[o] = s[p].mul * (s[p].r - s[p].l + 1);

    pair <int, unsigned> w = make_pair(s[p].r - s[p].l + 1, 1);
    s[p].T[o].push_back(w);
    s[p].L[o].push_back(w);
    s[p].R[o].push_back(w);
    s[p].C[o].push_back(w);

    o = !o;
    s[p].num[o] = s[p].sum[o] = s[p].res[o] = s[p].pre[o] = s[p].suf[o] = s[p].val[o][0] = s[p].val[o][1] = s[p].len[o][0] = s[p].len[o][1] = 0;
}

inline void Val(int p, unsigned a)
{
    s[p].cov = a;
    s[p].mul = Pow(a, s[p].r - s[p].l + 1);
    for(int o = 0; o < 2; ++ o)
    {
        s[p].val[o][0] = s[p].len[o][0] ? Pow(a, s[p].len[o][0]) : 0;
        s[p].val[o][1] = s[p].len[o][1] ? Pow(a, s[p].len[o][1]) : 0;
        s[p].num[o] = s[p].res[o] = s[p].sum[o] = s[p].pre[o] = s[p].suf[o] = 0;
        for(auto u : s[p].T[o])
            H[u.first] = Pow(a, u.first) * u.first, s[p].res[o] += u.second * H[u.first];
        for(auto u : s[p].L[o])
            s[p].pre[o] += u.second * H[u.first];
        for(auto u : s[p].R[o])
            s[p].suf[o] += u.second * H[u.first];
        for(auto u : s[p].C[o])
            s[p].sum[o] += u.second * H[u.first], s[p].num[o] += u.second, H[u.first] = 0;
    }
}

inline void push_down(int p)
{
    if(s[p].rev)
    {
        Rev(ls);
        Rev(rs);
        s[p].rev = 0;
    }
    if(s[p].cov && s[p].sgn)
    {
        Cov(ls, s[p].cov, s[p].sgn);
        Cov(rs, s[p].cov, s[p].sgn);
        s[p].cov = s[p].sgn = 0;
    }
    if(s[p].cov)
    {
        Val(ls, s[p].cov);
        Val(rs, s[p].cov);
        s[p].cov = 0;
    }
    if(s[p].sgn)
    {
        Sign(ls, s[p].sgn);
        Sign(rs, s[p].sgn);
        s[p].sgn = 0;
    }
}

void build(int p, int l, int r)
{
    s[p].l = l;
    s[p].r = r;
    if(l == r)
    {
        if(b[l] != 0)
        {
            int o = max(b[l], 0);
            pair <int, unsigned> w = make_pair(1, 1);
            s[p].T[o].push_back(w);
            s[p].L[o].push_back(w);
            s[p].R[o].push_back(w);
            s[p].C[o].push_back(w);
            s[p].num[o] = s[p].len[o][0] = s[p].len[o][1] = 1;
            s[p].sum[o] = s[p].res[o] = s[p].pre[o] = s[p].suf[o] = s[p].val[o][0] = s[p].val[o][1] = s[p].mul = a[l];
        }
        return;
    }
    int mid = l + r >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    s[p] = s[ls] + s[rs];
}

void updCov(int p, int l, int r, int x, int y)
{
    if(s[p].l >= l && s[p].r <= r)
        return Cov(p, x, y);
    push_down(p);
    int mid = s[p].l + s[p].r >> 1;
    if(l <= mid)
        updCov(ls, l, r, x, y);
    if(r > mid)
        updCov(rs, l, r, x, y);
    s[p] = s[ls] + s[rs];
}

void updSgn(int p, int l, int r, int x)
{
    if(s[p].l >= l && s[p].r <= r)
        return Sign(p, x);
    push_down(p);
    int mid = s[p].l + s[p].r >> 1;
    if(l <= mid)    
        updSgn(ls, l, r, x);
    if(r > mid)
        updSgn(rs, l, r, x);
    s[p] = s[ls] + s[rs];
}

void updRev(int p, int l, int r)
{
    if(s[p].l >= l && s[p].r <= r)
        return Rev(p);
    push_down(p);
    int mid = s[p].l + s[p].r >> 1;
    if(l <= mid)    
        updRev(ls, l, r);
    if(r > mid)
        updRev(rs, l, r);
    s[p] = s[ls] + s[rs];
}

void updVal(int p, int l, int r, int x, int y)
{
    if(s[p].l >= l && s[p].r <= r)
    {
        Val(p, x);
        if(y == -1)
            Rev(p);
        return;
    }
    push_down(p);
    int mid = s[p].l + s[p].r >> 1;
    if(l <= mid)
        updVal(ls, l, r, x, y);
    if(r > mid)
        updVal(rs, l, r, x, y);
    s[p] = s[ls] + s[rs];
}

Tree get(int p, int l, int r)
{
    if(s[p].l >= l && s[p].r <= r)
        return s[p];
    push_down(p);
    int mid = s[p].l + s[p].r >> 1;
    if(r <= mid)
        return get(ls, l, r);
    if(l > mid)
        return get(rs, l, r);
    return get(ls, l, r) + get(rs, l, r);
}

void init(int y, unsigned x)
{
    I[0][y] = 1;
    for(int i = 1; i <= 130; ++ i)
        I[i][y] = I[i - 1][y] * x;
    J[0][y] = 1;
    for(int i = 1; i <= 130; ++ i)
        J[i][y] = J[i - 1][y] * I[130][y];
}

int idx;

int main()
{
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);

    scanf("%d%d", &n, &m);
    for(int i = 1, x; i <= n; ++ i)
        scanf("%d", &x), a[i] = Abs(x), b[i] = Sgn(x);

    build(1, 1, n);

    for(int i = 1, o, l, r, x; i <= m; ++ i)
    {
        scanf("%d%d%d", &o, &l, &r);
        if(o != 5)
            scanf("%d", &x);
        if(o == 1)
            init(++ idx, Abs(x)), updCov(1, l, r, idx, Sgn(x));
        if(o == 2)
            updSgn(1, l, r, Sgn(x));
        if(o == 3 && x < 0)
            updRev(1, l, r);
        if(o == 4)
            init(++ idx, Abs(x)), updVal(1, l, r, idx, Sgn(x));
        if(o == 5)
            printf("%u\n", get(1, l, r).res[1]);
    }

    return 0;
}

9.24

CSP 4

A

link

题目描述

你需要找到 \(l,r\),使得 \(l\le r\le l+d\)。构造序列 \(b_1,b_2,\cdots,b_n\)​,其中

\[b_i=\left\{ \begin{aligned} &l &\quad a_i\le l\\ &a_i &\quad l<a_i<r\\ &r &\quad a_i \ge r \end{aligned} \right. \]

你要求出 \(\sum_{i=1}^{n−1} \lvert b_i−b_{i+1}\rvert\) 的最大值。

记得读清题,是 \(r \le l + d\) 不是 \(r \le l + d - 1\)

猜凸性,三分,然后过了。但是不知道真假。

对于每个 \(i\),考虑 \(a_i,a_{i + 1}\) 对哪些 \(l\) 造成贡献。发现是一个分段函数,记录一下扫描线即可。

*C

link

题目描述

有一张 \(2\times n\) 的网格图,你要从左上角 \((1,1)\) 走到右下角 \((2,n)\)。每条边有边权,并且有额外的 $ m$ 条限制。每条限制形如:给定 \(i,j,c\),如果你同时走了 \((1,i)\)\((1,i+1)\)\((2,j)\)\((2,j+1)\) 这两条边,那么你就需要额外 \(c\) 的代价。求走过的边权和加上代价的最小值。

\(1\le n \le 500\)\(1 \le m \le 1000\)

看数据范围,猜网络流,想最小割,切糕模型。

源点为 \(s\),汇点为 \(t\)

\(s \to i\) 流量 \(+ \infty\),不可割。\(i \to i + n\) 流量 \((1,i)\)\((1,i + 1)\) 之间的边权。\(i + n \to t\) 流量 \((2,i)\)\((2,i + 1)\) 之间的边权。割掉哪个就相当于走那条边。

\(i + n\)\(i + n + 1\) 之间建双向边,流量为 \((1, i + 1)\)\((2, i + 1)\) 的边权。如果 \(i\) 格子到 \(i + 1\) 格子选择走第一行,\(i + 1\)\(i + 2\) 选择第二行,网络流上需要割掉 \(i + n + 1 \to i + n\) 之间的边,否则还可以通过 \(s \to i + 1 \to i + n + 1 \to i + n \to t\) 从源点流向汇点。反过来同理,所以是双向边。

\(i, j, c\) 的限制连边 \(i + n, j + n, c\),原理同上。

*D

link

题目描述

给定 \(n,m\),对所有长度为 \(n\),值域为 \([1,m]\) 的序列求众数出现次数之和。对 \(10^9+7\) 取模。多组数据。

\(1\le T \le 10\)\(1 \le n \le 1000\)\(1 \le m \le 10^9\)

枚举 \(k\),计算所有数出现次数均 \(\le k\) 的方案数,然后统计答案。

考虑 \(\text{EGF}\),设 \(e_k(x) = \sum\limits_{i=0}^k \frac {x ^ i} {i !}\),答案为 \(n![x^n]e_k^m(x)\)

如果不理解可以考虑选出一个值域为 \([1,m]\) 的可重集。设 \(o_{k}=\sum\limits_{i=0}^k x^i\),答案为 \([x^n]o_k^m(x)\)

\[\begin{aligned} &(e_k^m(x))'\\ =&me_k^{m-1}e_k'(x)\\ =& me_k^{m-1}(x)(e_k(x) - \frac {x^k}{k!})\\ =& me_k^m(x) - \frac{mx^k}{k!}e_{k}^{m - 1}(x) \end{aligned} \]

\(f_{i, j}\) 表示 \([x^j]e_k^i\)

\[j\times f_{i, j} = i \times f_{i,j-1} - \frac{i\times f_{i-1,j-k-1}} {k!} \]

\(m\) 很大但是 \(n\) 很小。复杂度为 \(\sum_k O(\frac{n^2} k)=O(n^2 \log n)\)

代码
#include <cstdio>
#include <algorithm>

#define int long long

using namespace std;

const int N = 1005;
const int mod = 1e9 + 7;

int T, n, m, ans, fac[N], inv[N], ifa[N], f[2][N], g[N];

int Pow(int x, int y)
{
    int r = 1;
    for(; y; y >>= 1, x = x * x % mod)
        if(y & 1)
            r = r * x % mod;
    return r;
}

int calc(int k)
{
    for(int i = 0; i <= n; ++ i)
        f[0][i] = f[1][0] = 0;
    int t = n / k + (n % k == 0 ? 0 : 1);
    int l = max(0ll, m - t);
    f[l & 1][0] = 1; 
    for(int i = l + 1; i <= m; ++ i)
    {
        f[i & 1][0] = 1;
        for(int j = 1; j <= n - (m - i) * k; ++ j)
        {
            if(j - k - 1 >= 0)
                f[i & 1][j] = (i * f[i & 1][j - 1] - i * ifa[k] % mod * f[i - 1 & 1][j - k - 1]) % mod * inv[j] % mod;
            else
                f[i & 1][j] = i * inv[j] % mod * f[i & 1][j - 1] % mod;
        }
    }
    return f[m & 1][n];
}

void work()
{
    scanf("%lld%lld", &n, &m);
    for(int i = 1; i <= n; ++ i)
        g[i] = calc(i) * fac[n] % mod;

    // for(int i = 1; i <= n; ++ i)
    //     printf("%lld ", g[i]);
    // printf("\n");

    ans = 0;
    for(int i = 1; i <= n; ++ i)
        ans = (ans + (g[i] - g[i - 1]) * i) % mod;
    printf("%lld\n", (ans + mod) % mod);
}

signed main()
{
    freopen("capital.in", "r", stdin);
    freopen("capital.out", "w", stdout);

    fac[0] = inv[0] = inv[1] = ifa[0] = ifa[1] = 1;
    for(int i = 1; i <= 1000; ++ i)
        fac[i] = fac[i - 1] * i % mod;
    for(int i = 2; i <= 1000; ++ i)
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    for(int i = 2; i <= 1000; ++ i)
        ifa[i] = ifa[i - 1] * inv[i] % mod;

    scanf("%lld", &T);
    while(T --)
        work();

    return 0;
}

9.25

联考 1

A

link

题目描述

给定模式串 \(x,y\),给定串 \(s\),求是否可以把 \(s\) 划分成两个子序列,使得其中一个是 \(x\) 重复若干次(可能为空),另外一个是 \(y\) 重复若干次(可能为空)。多组数据。

保证 \(\sum\lvert s\rvert\le 5\times 10^5,\lvert x\rvert ,\lvert y\rvert \le50,T\le 10^4\)

直接 dp。可以 bitset 优化。

*B

link

题目描述

给定一棵树,小 q 想把所有树边都删掉。初始他会选择一个点开始,每次可以执行以下三种操作之一:

  1. 选择一条相邻的未被删除的边,经过这条边并将其删除。
  2. 花费 \(A\) 的代价,选择一条被删除的边将其恢复。
  3. 花费 \(B\) 的代价,瞬移至某个点。

求最少花费多少代价能够删除所有树边。

选择一些边加重边,设有 \(K\) 个奇度数的点,那么需要进行 \(B\) 操作的次数为 \(\frac K 2 - 1\)\(K\) 一定为偶数。

\(f_{u,0/1/2}\) 表示 \(u\) 子树加完重边后,\(u\) 子树没有奇点,或 \(u\) 点是奇点,或 \(u\) 点不是奇点的最小的代价。转移时比较进行 \(B\) 操作的次数和之前两个子树转移操作次数的和,有没有加一减一的变化。

代码
#include <cstdio>
#include <vector>
#include <algorithm>

#define int long long

using namespace std;

const int N = 500005;

int n, A, B, dp[N][3];

vector <int> e[N];

void dfs(int u, int fa)
{
    dp[u][0] = 0;
    dp[u][1] = dp[u][2] = 1e18;
    for(int v : e[u])
    {
        if(v == fa)
            continue;
        dfs(v, u);
        
        int t[3] = {dp[u][0], dp[u][1], dp[u][2]};

        dp[u][0] = dp[u][1] = dp[u][2] = 1e18;

        dp[u][1] = min(dp[u][1], t[0] + dp[v][0]);
        dp[u][0] = min(dp[u][0], t[0] + dp[v][0] + A);
        dp[u][1] = min(dp[u][1], t[0] + dp[v][1]);
        dp[u][2] = min(dp[u][2], t[0] + dp[v][1] + A);
        dp[u][1] = min(dp[u][1], t[0] + dp[v][2] + B);
        dp[u][2] = min(dp[u][2], t[0] + dp[v][2] + A);

        dp[u][2] = min(dp[u][2], t[1] + dp[v][0]);
        dp[u][1] = min(dp[u][1], t[1] + dp[v][0] + A);
        dp[u][2] = min(dp[u][2], t[1] + dp[v][1]);
        dp[u][1] = min(dp[u][1], t[1] + dp[v][1] + A + B);
        dp[u][2] = min(dp[u][2], t[1] + dp[v][2] + B);
        dp[u][1] = min(dp[u][1], t[1] + dp[v][2] + A + B);
        
        dp[u][1] = min(dp[u][1], t[2] + dp[v][0] + B);
        dp[u][2] = min(dp[u][2], t[2] + dp[v][0] + A);
        dp[u][1] = min(dp[u][1], t[2] + dp[v][1] + B);
        dp[u][2] = min(dp[u][2], t[2] + dp[v][1] + A + B);
        dp[u][1] = min(dp[u][1], t[2] + dp[v][2] + B + B);
        dp[u][2] = min(dp[u][2], t[2] + dp[v][2] + A + B); 
    }
}

signed main()
{
    freopen("analyse.in", "r", stdin);
    freopen("analyse.out", "w", stdout);

    scanf("%lld%lld%lld", &n, &A, &B);
    for(int i = 1, u, v; i < n; ++ i)
        scanf("%lld%lld", &u, &v), e[u].push_back(v), e[v].push_back(u);

    dfs(1, 0);

    printf("%lld\n", min({dp[1][0], dp[1][1], dp[1][2]}));

    return 0;
}

**C

link

题目描述

用以下经典的方式构造一棵树:对 \(i=2\sim n\),从 \(1\sim i-1\) 中随机选择一个点当作 \(i\) 的父亲。

设点 \(u\) 的子树大小的 \(k\) 次方为 \(f_u\)​,给定 \(a_u\)​,求 \(\sum_u a_uf_u\) 的期望。对 \(10^9+7\) 取模。

\(u\) 子树大小 \(k\) 次方的期望改为:在 \(u\) 子树中选 \(k\) 个点,可放回的方案数。也就是说对于每个序列 \(u_1,\cdots,u_k\)\(u\) 子树的概率。

加上 \(n + 1,n + 2, \cdots, n + k\)\(k\) 个点,这些点的父亲在 \([1,n]\) 中随机。父节点为我们选的 \(k\) 个点。

\(f_{i,j}\) 表示考虑父节点在 \([i,n + k]\) 中的所有点,没定父亲的点在 \([1,i - 1]\) 中,当前 \(n + 1, \cdots,n + k\) 合并到了 \(j\) 个子树中的概率。初始时 \(f_{n + 1, k} = 1\)

计算到 \(i\) 时,枚举这 \(j\) 个子树有多少个子树根的父节点为 \(i\),设为 \(t\)。此时 \(t \neq 0\)。转移系数是

\[\begin{aligned} &{j \choose k} \times \frac 1 {i^k} \times \frac {(i - 1)^{j - k}} {i^{j - k}}\\ =& {j \choose k} \times \frac {(i - 1)^{j - k}} {i^j} \end{aligned} \]

\(i\) 子树大小的 \(k\) 次方就是 \(f_{i, 1} \times n^k\)。乘 \(n^k\) 是因为 \(f\) 数组计算的是选到这 \(k\) 个点,且这 \(k\) 个点在 \(i\) 子树内的概率。而我们求的是随机 \(k\) 个点在 \(i\) 子树的概率。然后再转移有 \(0\) 个子树的父节点为 \(i\) 的情况。

代码
#include <cstdio>
#include <algorithm>

#define int long long

using namespace std;

const int N = 100025;
const int mod = 1e9 + 7;

int n, m, ans, a[N], fac[N], inv[N], ifa[N], dp[2][25];

int Pow(int x, int y)
{
    int r = 1;
    for(; y; y >>= 1, x = x * x % mod)
        if(y & 1)
            r = r * x % mod;
    return r;
}

int C(int n, int m)
{
    return fac[n] * ifa[m] % mod * ifa[n - m] % mod;
}

signed main()
{
    freopen("algebra.in", "r", stdin);
    freopen("algebra.out", "w", stdout);

    scanf("%lld%lld", &n, &m);
    for(int i = 1; i <= n; ++ i)
        scanf("%lld", &a[i]);

    fac[0] = inv[0] = inv[1] = ifa[0] = ifa[1] = 1;
    for(int i = 1; i <= n + m; ++ i)
        fac[i] = fac[i - 1] * i % mod;
    for(int i = 2; i <= n + m; ++ i)
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    for(int i = 2; i <= n + m; ++ i)
        ifa[i] = ifa[i - 1] * inv[i] % mod;

    dp[n + 1 & 1][m] = 1;
    for(int i = n + 1; i >= 2; -- i)
    {
        for(int j = 1; j <= m; ++ j)
            for(int k = 1; k <= j; ++ k)
                (dp[i - 1 & 1][j - k + 1] += C(j, k) * Pow(i - 2, j - k) % mod * Pow(inv[i - 1], j) % mod * dp[i & 1][j] % mod) %= mod;

        ans = (ans + dp[i - 1 & 1][1] * a[i - 1] % mod) % mod;

        for(int j = 1; j <= m; ++ j)
            (dp[i - 1 & 1][j] += dp[i & 1][j] * Pow(i - 2, j) % mod * Pow(inv[i - 1], j) % mod) % mod;

        for(int j = 0; j <= m; ++ j)
            dp[i & 1][j] = 0;
    }

    printf("%lld\n", ans * Pow(n, m) % mod);

    return 0;
}

放个暴力

对于每个 \(u\),设 \(f_{i,j}\) 表示考虑前 \(i\) 个点,子树大小为 \(j + 1\) 的方案数。

转移代码如下(from 5k_sync_closer):

f[i][0] = fac[i - 1];
for(int j = i + 1; j <= n; ++ j)
{
    f[j][0] = f[j - 1][0] * (j - 2) % mod;
    for(int o = 1; o <= n; ++ o)
        f[j][o] = (f[j - 1][o] * (j - o - 2) + f[j - 1][o - 1] * o) % mod;
}

jijidawangoceans_of_stars 的做法比标程好啊!

考虑 \(x\) 点,子树大小为 \(i\) 的方案数。

先选出 \(i - 1\) 个点在 \(x\) 子树内,方案数 \(n - x \choose i - 1\)

\(x\) 子树内的方案数,\((i - 1)!\)

\(x\) 子树外的方案数,\((n - i - 1)!\)

\(x\) 点的方案数,\(x - 1\)

总答案为

\[\begin{aligned} &{n-x\choose i - 1}(i - 1)!(n - i - 1)!(x - 1)\\ = & \frac {(n - x)!(i - 1)!(n - i - 1)!(x - 1)}{(i - 1)!(n - x - i + 1)!}\\ = & (n - x)!(x - 1)!{n - i - 1 \choose x - 2}\\ \end{aligned} \]

所求的为期望,\(x\) 点的子树大小的 \(k\) 次方的期望为

\[\begin{aligned} &\dfrac{\sum_{i=1}^{n - x+1} i^k(n-x)!(x-1)!{n - i - 1 \choose x - 2}}{\sum_{i=1}^{n - x + 1}(n-x)!(x-1)!{n - i - 1 \choose x - 2}}\\ = & \dfrac{\sum_{i=1}^{n - x+1} i^k{n - i - 1 \choose x - 2}}{\sum_{i=1}^{n - x + 1}{n - i - 1 \choose x - 2}}\\ \end{aligned} \]

分母是好求的,考虑分子。

\[\begin{aligned} & {\sum_{i=0}^{n-x+1}i^k{n - i - 1 \choose x - 2}}\\ = & {\sum_{i=x-2}^{n - 1}(n - i - 1)^k{i \choose x - 2}}{}\\ = & {\sum_{i = 0}^{n - 1}(n - i - 1)^k{i \choose x - 2}}{}\\ = & { \sum_{i = 0}^{n - 1} \sum_{j = 0}^k {k \brace j} j!{n - i - 1 \choose j}{i \choose x - 2}}{}\\ = & { \sum_{j = 0}^k {k \brace j} j!\sum_{i = 0}^{n - 1} {n - i - 1 \choose j}{i \choose x - 2}}{}\\ = & {{\sum_{j = 0}^k {k \brace j} j!}{n \choose x + j - 1}}{}\\ \end{aligned} \]

上指标范德蒙德卷积

\[\sum_{i=1}^{n+1} {i - 1\choose a} {n - i +1 \choose b} = {n + 1\choose a + b + 1} \]

\(n+1\) 个球,选 \(a+b+1\) 个球。枚举第 \(a+1\) 个球的位置。

D

link

loj 4022

题目描述

有两个隐藏的数字 \(a,b\)。保证 \(1\le a,b\le N\)。你需要构造一系列询问,每次询问包含一个数字集合,你可以知道 \(a,b\) 中是否存在至少一个数在集合。你需要确保你可以通过这些询问找到这个无序对 \((a,b)\)

询问次数 \(R \le 26\) 为满分。

构造 \(1000\)\(26\) 位二进制数,满足不同的 \((a,b)\)\(x_a \mid x_b\) 不同。

APIO 2024

随机选择 \(a_i\) 判断能否加入可以跑出 \(R \ge 29\) 的解。

\(a_i\) 每一个 bit 有 \(p\) 的概率是 \(1\),取 \(\frac 1 3\sim \frac 1 4\),可以得出 \(R = 28\)

选择 \(\text{popcount} = 8\) 的数随机,可得到 \(R = 27\)

从小到大加入,如果一个数与集合中的数按位与的 \(\text{popcount} \ge 6\),就不加入。

然后把一些数随机加入,删一些数,加一些数,就能得到满分。

原题的一些子任务还有一些其他问题。

9.27

CSP 5

A

题目描述

给定 \(4\) 个数 \(a, b, c, d\)。找到 \(4\) 个非负整数 \(x,y,z,w\),满足以下条件:

\[\begin{aligned} x+\lfloor \frac y 2 \rfloor + \lfloor \frac z 2 \rfloor + \lfloor \frac w 4 \rfloor \ge a\\ y+\lfloor \frac x 2 \rfloor + \lfloor \frac w 2 \rfloor + \lfloor \frac z 4 \rfloor \ge b\\ z+\lfloor \frac x 2 \rfloor + \lfloor \frac w 2 \rfloor + \lfloor \frac y 4 \rfloor \ge c\\ w+\lfloor \frac y 2 \rfloor + \lfloor \frac z 2 \rfloor + \lfloor \frac x 4 \rfloor \ge d\\ \end{aligned} \]

使得 \(x + y + z + w\) 最小。输出最小值。

\(1 \le a,b,c,d \le 1500\)

二分答案,枚举对角线的两个值,然后分情况讨论 \(\mod 4\) 的值。

说点其他的,比如一个二元一次不等式的解集是一个半平面。

还可以枚举 \(x+w,y+z\)

B

题目描述

鸡尾酒很喜欢让别人爬,于是他种了一棵树。然后在树上放了 \(n\) 只蚂蚁,看它们爬。

树上有 \(n\) 个点,用 \(n − 1\) 条边相连,除了根结点外,每个结点都有父亲,根结点记为 \(1\) 号结点,其它结点分别编号为 \(2 \sim n\)\(n\) 个点上分别放有一只蚂蚁,并且每只蚂蚁有一个属性 \(a_i\),除了根节点的蚂蚁之外,每只蚂蚁都可以选择向上爬到父亲结点或者不爬(只能爬一次)。在所有蚂蚁都选择完毕之后,我们统计这些蚂蚁产生的快乐值:如果多只蚂蚁在同一点,则产生这些蚂蚁属性的异或和的快乐值。如果某个点上只有一只蚂蚁或者没有蚂蚁,则不产生快乐值。问所有方案的所有快乐值之和。

树形 DP,按位考虑。先按照一只蚂蚁也有贡献做,再把一只蚂蚁的贡献减掉。比较复杂。

int_R 的做法很好啊。一个点的贡献只和它自己和儿子有关。设 \(s = \lvert son(u) \rvert + 1\)\(c_i\)\(u\) 及其儿子中第 \(i\) 位为 \(1\) 的个数,则 \(u\) 点的贡献为:

\[2^{n-1-s}\sum_{i}2^i(2^{c_i-1}2^{s-c_i}-{c_i \choose 1}{s - c_i \choose 0}) \]

有个式子

\[{n \choose 1} + {n \choose 3} + \cdots =2^{n-1} \]

C

题目描述

你有 \(n\) 个字母 \(A\)\(m\) 个字母 \(B\),你可以将这些字母组成成为一个字符串,你需
要使得这个字符串的权值尽量大。现在我们以如下规则计算这个字符串的权值。

  1. 每有连续的 \(a\)\(A\) ,且下一个字母依旧是 \(A\),则权值 \(+1\)。假设 \(a = 3\),且连续有 \(7\)\(A\),那么根据此规则,权值 \(+2\)。你可以理解一段长度为 \(C\)\(A\) 所获得的权值为 \(\lfloor \frac {C - 1} a\rfloor\)
  2. 每有连续的 \(b\)\(B\) ,且下一个字母依旧是 \(B\),则权值 \(+1\)
  3. 上一个字母和当前字母不一样时,权值 \(+1\)。(第一个字母前面没有字母,也会使得权值 \(+1\),详见样例 1)

假设当前字母是 \(B\),则至少需要有连续 \(c\) 个字母 \(B\),下一个字母才可以切换成 \(A\)。字母 \(A\) 切换到字母 \(B\) 没有任何限制。

请问你能构造的字符串权值最大可能是多少?

设字符串 \(s\)\(c\)\(B\),一个 \(A\)

枚举字符串 \(s\) 循环出现多少次,然后贪心把剩下的 \(A,B\) 字符用完。

\(A\) 字符先在最前面放一个,然后每有 \(a\)\(A\) 可以增加 \(1\) 的贡献。

\(B\) 字符可以先在最后放一个,然后把中间的 \(B\) 字符补成 \(kb + 1\) 的长度,然后每有 \(b\)\(B\) 可以增加 \(1\) 的贡献。

D

题目描述

鸡尾酒有一个奇怪的函数 \(F(x)\),这个函数的输入参数是一个正整数 \(x\),为了得到这个函数的运算结果,这个函数需要依次进行 \(n\) 个步骤,每个步骤是如下三种形式之一:

  1. \(x \leftarrow x + val\)
  2. \(x \leftarrow \min(x, val)\)
  3. \(x \leftarrow \max(x, val)\)

依次执行完这 \(n\) 个步骤之后,这个函数就可以安心输出答案了。

现在,鸡尾酒得到了这个函数,他想简化这个函数,确切的来说,他有 \(q\) 个问题,每个问题要么是修改这个函数的某一个步骤,要么给定一个 \(x\),询问当前 \(F(x)\) 的值,请帮助他完成这个过程。

发现是分段函数,形如:

\[F(x) = \begin{cases} A, &x \le L\\ x + T,& L < x < R\\ B,& x \ge R\\ \end{cases} \]

证明考虑归纳。

线段树维护分段函数即可。

重要的不是这个,考虑构造 \((\min, +)\) 矩阵,\(C_{i,j} = \min_k(A_{i,k}+B_{k,j})\)

当前数字 \(x\)\(val\)\(\min\)

\[\begin{bmatrix} x & 0\\ \end{bmatrix} \]

\[\begin{bmatrix} 0 & \inf\\ val & 0\\ \end{bmatrix} \]

当前数字加 \(val\)

\[\begin{bmatrix} val & \inf\\ \inf & 0\\ \end{bmatrix} \]

如果只有 \(1,2\) 操作,可以直接动态 DP。

代码
#include <cstdio>
#include <algorithm>

#define ls p << 1
#define rs p << 1 | 1

using namespace std;

const int N = 100005;
const int inf = 1000000000;

int n, m, a[N], b[N];

struct Node
{
    int l, r, o, x;
};

struct Tree
{
    int l, r, c;
    Node t[3];
};
Tree s[N << 2];

Node f[10];

void push_up(int p)
{
    int k = -1;

    for(int i = 0; i < s[ls].c; ++ i)
    {
        for(int j = 0; j < s[rs].c; ++ j)
        {
            if(s[ls].t[i].o == 1)
            {
                if(s[ls].t[i].x >= s[rs].t[j].l && s[ls].t[i].x <= s[rs].t[j].r)
                {
                    if(s[rs].t[j].o == 1)
                        f[++ k] = {s[ls].t[i].l, s[ls].t[i].r, 1, s[rs].t[j].x};
                    else
                        f[++ k] = {s[ls].t[i].l, s[ls].t[i].r, 1, s[ls].t[i].x + s[rs].t[j].x};
                }
            }
            else
            {
                int L = s[ls].t[i].l + s[ls].t[i].x, R = s[ls].t[i].r + s[ls].t[i].x;
                if(L <= s[rs].t[j].r && R >= s[rs].t[j].l)
                {
                    L = max(L, s[rs].t[j].l) - s[ls].t[i].x;
                    R = min(R, s[rs].t[j].r) - s[ls].t[i].x;
                    if(s[rs].t[j].o == 1)
                        f[++ k] = {L, R, 1, s[rs].t[j].x};
                    else
                        f[++ k] = {L, R, 2, s[ls].t[i].x + s[rs].t[j].x};
                }
            }
        }
    }

    s[p].c = 0;
    for(int i = 0; i <= k; ++ i)
    {
        if(i != 0 && f[i].o == f[i - 1].o && f[i].x == f[i - 1].x)
            s[p].t[s[p].c - 1].r = f[i].r;
        else
            s[p].t[s[p].c ++] = f[i];
    }
    s[p].t[0].l = 1;
    s[p].t[s[p].c - 1].r = inf;
}

void upd(int p, int l)
{
    s[p].c = 0;
    if(a[l] == 1)
        s[p].t[0] = {1, inf, 2, b[l]}, s[p].c = 1;
    if(a[l] == 2)
    {
        if(b[l] != 1)
            s[p].t[s[p].c ++] = {1, b[l] - 1, 2, 0};
        s[p].t[s[p].c ++] = {b[l], inf, 1, b[l]};
    }
    if(a[l] == 3)
    {
        s[p].t[s[p].c ++] = {1, b[l], 1, b[l]};
        s[p].t[s[p].c ++] = {b[l] + 1, inf, 2, 0};
    }
}

void build(int p, int l, int r)
{
    s[p].l = l;
    s[p].r = r;
    if(l == r)
        return upd(p, l);
    int mid = l + r >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    push_up(p);
}

void update(int p, int x)
{
    if(s[p].l == s[p].r)
        return upd(p, x);
    int mid = s[p].l + s[p].r >> 1;
    if(x <= mid)
        update(ls, x);
    else
        update(rs, x);
    push_up(p);
}

int main()
{
    freopen("function.in", "r", stdin);
    freopen("function.out", "w", stdout);
    
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i)
        scanf("%d%d", &a[i], &b[i]);

    build(1, 1, n);

    scanf("%d", &m);
    for(int i = 1, o, x, y; i <= m; ++ i)
    {
        scanf("%d%d", &o, &x);
        if(o <= 3)
            scanf("%d", &y), a[x] = o, b[x] = y, update(1, x);
        else
        {
            for(int j = 0; j < s[1].c; ++ j)
            {
                if(x >= s[1].t[j].l && x <= s[1].t[j].r)
                {
                    printf("%d\n", s[1].t[j].o == 1 ? s[1].t[j].x : (s[1].t[j].x + x));
                    break;
                }
            }
        }
    }

    return 0;
}

link

翻洛谷讨论区,看到一道题,第二天写的博客。

9.28

CSP 6

A

DP。

B

分情况讨论。删掉一个数是否对前面有影响。思考清楚。

*D

题目描述

现在给定 \(n,k,p\),请你求出 \(n\) 个点的无标号无根树中,直径长度为 \(k\) 且恰有 \(p\) 条直径的树有多少?

\(1 \le n,p \le 40\)\(1 \le k < n\)

考虑转换成以直径中点为根的有根树个数。

\(f_{i,j,k}\) 表示子树节点数为 \(i\),深度为 \(k\) 的节点数为 \(j\),最深深度为 \(k\) 的有根树个数。称深度最深的叶子节点为有效叶子。

我们把节点数为 \(x\),深度为 \(z\) 的节点数为 \(y\),最深深度为 \(z\) 的有根树用三元组 \((x,y,z)\) 表示。

\(t\)\(v(x,y,z)\) 接到 \(u(i,j,k)\) 节点上,即令 \(t\) 棵状态为 \((x,y,z)\) 的树的父节点为 \(u\)。这个的方案数并不是 \(f_{i,j,k}\times f_{x,y,z}^t\)。因为状态为 \((x,y,z)\) 的树有很多种,相当于在 \(f_{x,y,z}\) 棵树中可重复选择 \(t\) 棵。答案为 \(f_{x,y,z} + t - 1 \choose t\)

对于每一种 \((x,y,z)\),选择的数目不同,当做不同种物品。每一种 \((x,y,z)\) 的不同物品最多选 \(1\) 个。相当于分组背包。

这样就可以转移 \(f\) 了。

对于直径长度为奇数来说,直径中点是一条边。枚举两边子树大小和有效叶子数即可。

对于偶数,设 \(g_{i,j,k}\) 表示子树节点数为 \(i\),有效叶子数为 \(j\),直径数目为 \(k\) 的答案。转移类似上面。

代码
#include <cstdio>
#include <algorithm>

#define int long long

using namespace std;

const int N = 45;
const int mod = 998244353;

int n, len, num, ans, fac[N], inv[N], ifa[N], f[N][N][N], g[N][N][N];

int C(int n, int m)
{
    int res = 1;
    for(int i = n - m + 1; i <= n; ++ i)
        res = i % mod * res % mod;
    return res * ifa[m] % mod;
}

signed main()
{
    freopen("dia.in", "r", stdin);
    freopen("dia.out", "w", stdout);

    scanf("%lld%lld%lld", &n, &len, &num);
    
    fac[0] = inv[0] = inv[1] = ifa[0] = ifa[1] = 1;
    for(int i = 1; i <= 40; ++ i)
        fac[i] = fac[i - 1] * i % mod;
    for(int i = 2; i <= 40; ++ i)
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    for(int i = 2; i <= 40; ++ i)
        ifa[i] = ifa[i - 1] * inv[i] % mod;

    f[1][1][0] = 1;

    // f[i][j][k]: 子树大小为 i,有效叶子数为 j,深度为 k 的方案数
    for(int a = 1; a <= n; ++ a)
    {
        for(int b = 1; b <= a; ++ b)
        {
            for(int c = 0; c <= len / 2; ++ c)
            {
                if(!f[a][b][c])
                    continue;
                for(int i = n; i >= 1; -- i)
                {
                    for(int j = 1; j <= i; ++ j)
                    {
                        for(int k = 0; k <= len / 2; ++ k)
                        {
                            if(!f[i][j][k])
                                continue;
                            for(int t = 1; i + a * t <= n && j + b * t <= n; ++ t)
                            {
                                int A = i + a * t, B, D = max(c + 1, k);
                                if(i == 1)
                                    B = b * t;
                                else if(c + 1 == k)
                                    B = j + b * t;
                                else if(c + 1 > k)
                                    B = b * t;
                                else
                                    B = j;
                                (f[A][B][D] += f[i][j][k] * C(f[a][b][c] + t - 1, t)) %= mod;
                            }
                        }
                    }
                }
            }
        }
    }

    if(len & 1)
    {
        for(int a = 1; a <= n / 2; ++ a)
        {
            for(int b = 1; b <= a; ++ b)
            {
                if(!f[a][b][len / 2])
                    continue;
                for(int c = 1; c <= n - a; ++ c)
                {
                    if(b * c == num && (n - a != a || b <= c))
                        ans = (ans + f[a][b][len / 2] * f[n - a][c][len / 2]) % mod;
                }
            }
        }
        printf("%lld\n", ans);
        return 0;
    }

    // g[i][j][k]: 子树大小为 i,有效叶子数 j,k 条直径的方案数

    g[1][0][0] = 1;
    for(int a = 1; a <= n; ++ a)
    {
        for(int b = 1; b <= a; ++ b)
        {
            for(int c = 0; c <= len / 2 - 1; ++ c)
            {
                if(!f[a][b][c])
                    continue;
                for(int i = n; i >= 1; -- i)
                {
                    for(int j = 0; j <= i; ++ j)
                    {
                        for(int k = 0; k <= num; ++ k)
                        {
                            if(!g[i][j][k])
                                continue;
                            for(int t = 1; i + a * t <= n && j + b * t <= n; ++ t)
                            {
                                int B = j + ((c == len / 2 - 1) ? b * t : 0);
                                int D = k + ((c == len / 2 - 1) ? t * b * j + t * (t - 1) / 2 * b * b : 0);
                                if(D <= num)
                                    (g[i + a * t][B][D] += g[i][j][k] * C(f[a][b][c] + t - 1, t)) %= mod;
                            }
                        }
                    }
                }
            }
        }
    }
    
    for(int j = 1; j <= n; ++ j)
        ans = (ans + g[n][j][num]) % mod;
    printf("%lld\n", (ans + mod) % mod);

    return 0;
}
posted @ 2024-09-30 21:44  Estelle_N  阅读(50)  评论(0编辑  收藏  举报