当前位置: 首页 > article >正文

1.23 补题 寒假训练营

E 一起走很长的路!


输入描述

第一行输入两个整数 n,q(1≤n,q≤2×10^5),代表多米诺骨牌的个数和询问次数。 

第二行输入 n 个整数 a1,a2,…,an​(1≤ai≤10^9),表示多米诺骨牌的重量。

此后输入 q 行,每行输入两个整数 l,r(1≤l≤r≤n),代表一次询问。


输出描述

对于每一次询问,新起一行。输出一个整数,代表牛可乐最少需要调整的次数。每一次询问相互独立。


示例

输入:
4 2
2 1 2 7
2 3
1 4
输出:
1
2
说明:
  • 对于第一次询问,其中一种最优的调整方案是:将第 3 张多米诺骨牌的重量减少 1。

  • 对于第二次询问,其中一种最优的调整方案是:将第 3 张多米诺骨牌的重量增加 1、将第 4 张多米诺骨牌的重量减少 1,此时,各张多米诺骨牌的重量分别为 2, 1, 3, 6,符合题意,游戏完美。


思路

  1. 前缀和计算:首先计算数组 a 的前缀和 s,即 s[i] = s[i-1] + a[i]。前缀和的作用是快速计算任意区间的和。

  2. 构建线段树:我们需要构建一个线段树来维护区间内的最大值。线段树的每个节点维护以下信息:

    • l 和 r:表示当前节点所代表的区间范围。

    • maxv:表示当前区间内的最大值。

    • add:表示当前区间内的延迟标记(用于区间更新)。

  3. 初始化线段树:在构建线段树时,我们初始化每个叶子节点的值为 w[i] = a[i] - s[i-1]。这是因为我们需要维护的表达式是 a[i] - s[i-1]

  4. 查询处理:对于每个查询 [l, r],我们需要计算区间 [l+1, r] 内的最大值。具体步骤如下:

    • 首先,我们计算 tmp = s[l-1],即区间 [1, l-1] 的和。

    • 然后,我们对区间 [l+1, r] 进行区间更新,将每个元素的值增加 tmp。这是因为我们需要计算的是 a[i] - s[i-1] + s[l-1],即 a[i] - (s[i-1] - s[l-1])

    • 接着,我们查询区间 [l+1, r] 的最大值,并将其与 0 取最大值(因为题目要求输出非负数)。

    • 最后,我们将区间 [l+1, r] 的值恢复原状,即减去 tmp


代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, q;
int a[200005], s[200005], w[200005];
#define lc (p << 1)
#define rc (p << 1 | 1)
struct Node {
    int l, r;
    int maxv; 
    int add;  
} tr[800005];
void pushup(int p) {
    tr[p].maxv = max(tr[lc].maxv, tr[rc].maxv);
}
void pushdown(int p) {
    if (tr[p].add != 0) {
        tr[lc].maxv += tr[p].add; 
        tr[rc].maxv += tr[p].add;
        tr[lc].add += tr[p].add; 
        tr[rc].add += tr[p].add; 
        tr[p].add = 0;            
    }
}
void build(int p, int l, int r) {
    tr[p] = {l, r, w[l], 0};
    if (l == r) return;
    int m = (l + r) >> 1;
    build(lc, l, m);
    build(rc, m + 1, r);
    pushup(p);
}
//区间更新
void modify(int p, int x, int y, int k) {
    if (x <= tr[p].l && tr[p].r <= y) {
        tr[p].maxv += k;
        tr[p].add += k;
        return;
    }
    pushdown(p); 
    int m = (tr[p].l + tr[p].r) >> 1;
    if (x <= m) modify(lc, x, y, k);
    if (y > m) modify(rc, x, y, k);
    pushup(p);
}
int query(int p, int x, int y) {
    if (x <= tr[p].l && tr[p].r <= y) {
        return tr[p].maxv;
    }
    pushdown(p);
    int m = (tr[p].l + tr[p].r) >> 1;
    int res = -1e9; 
    if (x <= m) res = max(res, query(lc, x, y));
    if (y > m) res = max(res, query(rc, x, y));
    return res;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        s[i] = s[i - 1] + a[i];
    }
    for (int i = 1; i <= n; i++) {
        w[i] = a[i] - s[i - 1];
    }
    build(1, 1, n);
    while (q--) {
        int l, r;
        cin >> l >> r;
        if (l == r) {
            cout << 0 << '\n';
            continue;
        }
        int tmp = s[l - 1];
        modify(1, l + 1, r, tmp);
        int ans = max(0LL, query(1, l + 1, r));
        cout << ans << '\n';
        modify(1, l + 1, r, -tmp);
    }
    return 0;
}

C 字符串外串

题目描述

牛可乐定义字符串 ss 的可爱度为最大的整数 k,满足存在一个长度为 k 的连续子串 a,和一个长度为 k 的不连续子序列 b,满足 a=b。

现在,牛可乐给定你两个整数 n,m,并且询问你是否存在长度为 n、仅由小写字母构成的字符串 t,使得 t 的可爱度恰好等于 m。如果存在,输出任意一个符合条件的字符串 t。

定义:
  1. 连续子串:从原字符串中连续选择一段字符(可以全选、可以不选)得到的新字符串。

  2. 不连续子序列:至少由两段不相邻的非空子串构成。

输入描述:

每个测试文件均包含多组测试数据。第一行输入一个整数 T(1≤T≤10^4)代表数据组数,每组测试数据描述如下:

在一行上输入两个整数 n,m(3≤m≤n≤2×10^5)代表限制。

除此之外,保证单个测试文件的 n 之和不超过 2×10^5。

输出描述:

如果答案不存在,直接输出 NO;否则,在第一行输出 YES,在第二行输出一个仅由小写字母构成的字符串 ss,代表构造出的字符串。


示例

输入:

2
4 3
3 3

输出:

YES
abcc
NO

解释:

1.对于第一组测试数据:

字符串 abcc 的连续子串 abc 和不连续子序列 abc 满足条件。

因此,可爱度为 3,符合要求。

2.对于第二组测试数据:

无法构造满足条件的字符串,输出 NO


数据范围

  • 1≤T≤10^4

  • 3≤m≤n≤2×10^5

  • 单个测试文件的 n 之和不超过 2×10^5


思路

如果 n==m,则无法构造满足条件的字符串,因为不连续子序列至少需要两段不相邻的子串,而连续子串的长度为 n,无法满足条件。

如果 n−m>26,则无法构造满足条件的字符串,因为字母种类有限,无法保证不连续子序列的构造。

否则,可以通过构造一个字符串,使得其连续子串和不连续子序列的长度为 m。

构造方法

使用循环字母表构造字符串,使得字符串的前 n−m 个字符是唯一的,后面的字符重复前面的字符。

这样可以保证存在一个长度为 m 的连续子串和一个长度为 m 的不连续子序列。

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
int t;
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> t;
    while (t--)
    {
        int n, m;
        cin >> n >> m;
        if (n == m || n - m > 26)
        {
            cout << "NO" << "\n";
            continue;
        }
        cout << "YES" << "\n";
        for (int i = 0; i < n; i++)
        {
            cout << (char)('a' + i % (n - m));
        }
        cout << "\n";
    }
}

M 那是我们的影子 

示例

输入:

4
6
1???56
456789
7891??
3
???
???
?11
3
123
456
789
3
723
18?
?9?

输出:

2
0
1
6

解释:

  1. 对于第一组测试数据,合法的情况有两种:

    • 第一种:

      1 2 3 4 5 6
      4 5 6 7 8 9
      7 8 9 1 2 3
    • 第二种:

      1 3 2 4 5 6
      4 5 6 7 8 9
      7 8 9 1 3 2
  2. 对于第二组测试数据,由于初始的矩阵中存在两个 1,所以无论如何构造都不合法。

  3. 对于第三组测试数据,不需要填入任何数字,所以只有唯一一种合法解。

  4. 对于第四组测试数据,有 6 种合法的填法。


数据范围

  • 1≤T≤100

  • 3≤n≤10^5

  • 单个测试文件的 n 之和不超过 3×10^5


思路

代码 

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<string> s(3);
        for (auto &i : s) cin >> i;
        int flag = 1;
        vector<set<int>> st(3);
        vector<int> a(n);
        for (int j = 0; j < n; j++) {
            set<int> t;
            for (int i = 0; i < 3; i++) {
                if (s[i][j] == '?') {
                    a[j]++;
                    continue;
                }
                s[i][j] -= '0';
                if (t.count(s[i][j])) flag = 0;
                t.insert(s[i][j]);
            }
            st[j % 3].merge(t);
        }

        vector<int> sst;
        for (auto &i : st) {
            if (i.size() > 3) flag = 0;
            int x = 0;
            for (auto &j : i) x ^= 1 << j;
            sst.push_back(x);
        }

        if (!flag) {
            cout << 0 << endl;
            continue;
        }

        int S = 1;
        for (int i = 3; i < n; i++) {
            if (a[i] == 2) S = S * 2 % MOD;
            if (a[i] == 3) S = S * 6 % MOD;
        }

        vector<int> v(9);
        iota(v.begin(), v.end(), 1);
        int sum = 0;

        do {
            int flag = 1;
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    if (s[i][j] == '?') continue;
                    int x = i + j * 3;
                    if (s[i][j] != v[x]) flag = 0;
                }
            }

            int x;
            x = (1 << v[0]) + (1 << v[1]) + (1 << v[2]);
            if (x != (x | sst[0])) flag = 0;
            x = (1 << v[3]) + (1 << v[4]) + (1 << v[5]);
            if (x != (x | sst[1])) flag = 0;
            x = (1 << v[6]) + (1 << v[7]) + (1 << v[8]);
            if (x != (x | sst[2])) flag = 0;

            if (!flag) continue;
            sum = (sum + S) % MOD;
        } while (next_permutation(v.begin(), v.end()));

        cout << sum << '\n';
    }
    return 0;
}

I 一起看很美的日落!

题目描述

牛可乐有一棵由 n 个结点构成的树,第 i 个节点的权值为 ai。

我们定义一个连通块 V 的权值为:

  • 当前连通块中两两结点的权值异或和,即 ∑i,j∈V(ai⊕aj)。

你需要计算全部连通块的权值之和。由于答案可能很大,请将答案对 10^9+7 取模后输出。

此题中的连通块定义为:对于树上的任意一个点集 S,如果 S 中的任意两点 u,v 之间存在一条路径,且路径上的所有点都在 S 中,则称 S 是一个连通块。


输入描述

第一行输入一个整数 n(1≤n≤105),代表树上的节点数量。

第二行输入 n 个整数 a1,a2,…,an(1≤ai≤109),代表每个节点的权值。

此后 n−1 行,第 i 行输入两个整数 ui,vi(1≤ui,vi≤n,ui≠vi),代表树上第 i 条边连接节点 ui 和 vi。


输出描述

输出一个整数,代表全部连通块的权值和。答案可能很大,请将答案对 10^9+7 取模后输出。


示例 1

输入
3
5 2 1
1 2
1 3
输出
50
解释

在这个样例中,一共有 6 个连通块,每一个连通块的权值依次为:

  • {1},记为 V1​,权值为 a1⊕a1=0;

  • {1,2},记为 V2,权值为 (a1⊕a1)+(a1⊕a2)+(a2⊕a1)+(a2⊕a2)=14;

  • {1,3},记为 V3,权值为 (a1⊕a1)+(a1⊕a3)+(a3⊕a1)+(a3⊕a3)=8;

  • {1,2,3},记为 V4,权值为 28;

  • {2},记为 V5,权值为 a2⊕a2=0;

  • {3},记为 V6​,权值为 a3⊕a3=0。

所以,全部连通块的权值和为 0+14+8+28+0+0=50。


示例 2

输入
4
5 6 3 1
1 2
1 3
2 4
输出
142

思路

每个连通块的权值是所有点对的异或和。

直接枚举所有连通块并计算权值会超时,因为连通块的数量是指数级的。

需要利用树的性质和动态规划来优化计算。

动态规划

定义 dp[u]dp[u] 表示以 u 为根的子树中所有连通块的权值和。

定义 cnt[b][w][u] 表示以 u 为根的子树中,第 w 位为 b 的节点数。

定义 sz[u] 表示以 u 为根的子树中节点数。

DFS 遍历

在 DFS 过程中,更新 dp[u]、cnt[b][w][u] 和 sz[u]。

对于每个子节点 v,计算其对 dp[u] 的贡献,并更新 cnt[b][w][u] 和 sz[u]。

最终答案

所有连通块的权值和为 ans×2mod  (10^9+7)

 代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
const int N = 1e5 + 1;
int dp[N]; 
int cnt[2][30][N];
int sz[N]; 
int ans = 0; 
void dfs(int u, int fa, const vector<vector<int>>& adj, const vector<int>& a) {
    sz[u] = 1;
    for (int w = 0; w < 30; w++) {
        if ((a[u] >> w) & 1) {
            cnt[1][w][u] = 1;
        } else {
            cnt[0][w][u] = 1;
        }
    }
    for (int v : adj[u]) {
        if (v == fa) continue;
        dfs(v, u, adj, a);
        dp[u] = (dp[u] + 1LL * dp[v] * sz[u] % mod + 1LL * dp[u] * sz[v] % mod) % mod;
        for (int w = 0; w < 30; w++) {
            int t = (1 << w) % mod;
            dp[u] = (dp[u] + 1LL * t * cnt[0][w][u] % mod * cnt[1][w][v] % mod) % mod;
            dp[u] = (dp[u] + 1LL * t * cnt[1][w][u] % mod * cnt[0][w][v] % mod) % mod;
            cnt[0][w][u] = (cnt[0][w][u] + 1LL * cnt[0][w][u] * sz[v] % mod + 1LL * cnt[0][w][v] * sz[u] % mod) % mod;
            cnt[1][w][u] = (cnt[1][w][u] + 1LL * cnt[1][w][u] * sz[v] % mod + 1LL * cnt[1][w][v] * sz[u] % mod) % mod;
        }
        sz[u] = (sz[u] + 1LL * sz[v] * sz[u] % mod) % mod;
    }
    ans = (ans + dp[u]) % mod;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<int> a(n + 1);
    vector<vector<int>> adj(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        assert(a[i] >= 1 && a[i] <= 1e9);
    }
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    memset(dp, 0, sizeof(dp));
    memset(cnt, 0, sizeof(cnt));
    memset(sz, 0, sizeof(sz));
    ans = 0;
    dfs(1, 0, adj, a);
    cout << ans * 2 % mod << '\n';
    return 0;
}


http://www.kler.cn/a/520652.html

相关文章:

  • 【16届蓝桥杯寒假刷题营】第2期DAY5
  • spark运行流程
  • 从入门到精通:RabbitMQ的深度探索与实战应用
  • 03-机器学习-数据获取
  • Linux 入门 常用指令 详细版
  • 自签证书的dockerfile中from命令无法拉取镜像而docker的pull命令能拉取镜像
  • 图的矩阵表示
  • GEE | Sentinel-2影像监督分类、精度评估并导出
  • XSLT 编辑 XML:深度解析与实际应用
  • React应用深度优化与调试实战指南
  • SQL 约束
  • 【详解】SVM的核心思想和具体概念
  • 【计算机网络】host文件
  • 【2024年华为OD机试】 (A卷,200分)- 最大化控制资源成本(JavaScriptJava PythonC/C++)
  • 正则表达式 - 命名捕获组
  • 【C语言学习】:C语言补充:转义字符,<<,>>操作符,IDE
  • 9.中断系统、EXTI外部中断
  • 软件开发中的密码学(国密算法)
  • 1_相向双指针_leetcode_167_1
  • UE学习日志#11GAS--ASC源码简要分析9 AbilitySystemGlobals分析2 初始化相关
  • Chapter 3-17. Detecting Congestion in Fibre Channel Fabrics
  • Java多线程与高并发专题——保障原子性
  • 【FreeRTOS 教程 五】FreeRTOS 内存管理细致讲解
  • easyexcel-导入(读取)(read)-示例及核心部件
  • 记录让cursor帮我给ruoyi-vue后台管理项目整合mybatis-plus
  • 第05章 04 VTK标量算法概述