首页 > 其他分享 >Training Records 2

Training Records 2

时间:2024-09-30 21:45:40浏览次数:1  
标签:Training le rs int sum Records ls dp

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 k! \times f_{i-1,j-k-1}} {j} \]

\(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;
}

标签:Training,le,rs,int,sum,Records,ls,dp
From: https://www.cnblogs.com/Estelle-N/p/18438342

相关文章

  • Training Records 3
    9.30CSP7Alink题目描述给定\(5\)个长度为\(n\)的整数序列\(A,B,C,D,E\),求\[\sum_{i=1}^n\sum_{j=1}^n\sum_{k=1}^n\sum_{l=1}^l\sum_{m=1}^nmed(A_i,B_j,C_k,D_l,E_m)\mod998244353\]其中,\(med(a,b,c,d,e)\)为\(a,b,c,d,e\)的中位数。枚举中位数,计算即可......
  • 2024 Autumn Training #1 DF (by hzy)
    D.咸鱼跑酷(解有限trick)大意:长度n跑道,每个点可以二选一道具(+or*一个正数),q个询问从初始分数u,从l跑到r,求最大分数(结果模P)。可以预处理\(mul_i\)和\(add_i\),每个点要么乘要么加的数,把点分为两类,可乘点与不可乘点,\(mul_i=1\)意味着\(i\)点不可乘只能加,决策固定,因此我们需......
  • 2024 Autumn Training #2 CG (by hzy)
    C.Black-WhiteCubicLattice(网络流)大意:三维空间\(n*m*l\)格点黑白染色,已有初始色,每个点有翻转的代价\(w\),要求以最小的代价构造\((1,1,1)\)为黑,\((n,m,l)\)为白,且不存在内白外黑的点对。禁止内白外黑,考虑最小割,每个点向内连边\(inf\),白点流出\(w\),黑点流入\(w\),则最......
  • Pruning Large Language Models with Semi-Structural Adaptive Sparse Training
    本文是LLM系列文章,针对《PruningLargeLanguageModelswithSemi-StructuralAdaptiveSparseTraining》的翻译。通过半结构化自适应稀疏训练修剪大型语言模型摘要1引言2相关工作3方法4实验5结论摘要大型语言模型(LLM)在各种复杂任务中的巨大成功在很......
  • 《Learning Instance-Level Representation for Large-Scale Multi-Modal Pretraining
    系列论文研读目录文章目录系列论文研读目录摘要1.引言2.相关工作3.方法3.1.模型概述3.2.提取以实例为中心的表示法3.3.多模式预培训目标3.4.转移到下游任务4.实验预训练细节4.2.下游任务评价4.2.1零冲击产品分类4.2.2zero-shot图像-文本检索4.2.3零次产品检索4.2.4零......
  • [CVPR2024]DeiT-LT Distillation Strikes Back for Vision Transformer Training on L
    在长尾数据集上,本文引入强增强(文中也称为OOD)实现对DeiT的知识蒸馏的改进,实现尾部类分类性能的提升。动机ViT相较于CNN缺少归纳偏置,如局部性(一个像素与周围的区域关系更紧密)、平移不变性(图像的主体在图像的任意位置都应该一样重要)。因此需要大型数据集进行预训练。长尾数据学习......
  • 论文解读《MobileCLIP: Fast Image-Text Models through Multi-Modal Reinforced Trai
    系列文章目录文章目录系列文章目录论文细节理解1、研究背景2、论文贡献3、方法框架4、研究思路5、实验6、限制论文细节理解Ensembleteacher.在深度学习领域,什么意思?在深度学习领域,“ensembleteacher”通常指的是一种模型集成的方法,其中多个模型(教师模型)共同训......
  • 2017 ACM/ICPC Asia Regional Qingdao Online(SDKD 2024 Summer Training Contest J2)
    C-TheDominatorofStrings题意给定n个串,问是否有一个串包含其他所有串,有就输出这个串。思路如果有解,答案必定是最长串,一一比较即可。(没想到.find()就能过......
  • Python 之records教程
    目录Python之records教程一、安装二、初始化三、增,删,改,查1.增加2.删除(必须使用事务,不然不生效)3.修改(必须使用事务,不然不生效)4.查询Python之records教程一、安装pipinstallrecords二、初始化importrecords#初始化db连接,支持从环境变量DATABASE_URL读取url......
  • 2016 ACM/ICPC Asia Regional Qingdao Online(SDKD 2024 Summer Training Contest H2)
    A-ICountTwoThree题意给定n,求第一个\(\ge\)n的数k,且k=\(2^a3^b5^c7^d\)。思路考虑到样例很多,直接打表存入set省去数组排序操作,由于n$\le$1e9,所以只需要打到1e9后二分即可。(记得加上快读快写,T得饱饱的......