IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Codeforces Round #800 (Div. 2) A~E -> 正文阅读

[数据结构与算法]Codeforces Round #800 (Div. 2) A~E

Codeforces Round #800 (Div. 2)

[Link](Dashboard - Codeforces Round #800 (Div. 2) - Codeforces)

A. Creep

题意

? 让你用 a a a 0 0 0 b b b 1 1 1构造一个 01 01 01串,该串的前缀 0 , 1 0,1 0,1数量差的绝对值最小

思路

  • 贪心

? 贪心来看能抵消就抵消因此 0 , 1 0,1 0,1相间隔放,后面无法间隔就放剩下的即可。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        int x, y; cin >> x >> y;
        n = x + y;
        for (int i = 0; i < n; i ++) {
            if (x && i % 2 == 0) cout << 0, x --;
            else if (y && i % 2) cout << 1, y --;
            else if (x) cout << 0, x --;
            else cout << 1, y --;
        }
        cout << '\n';
    }
    return 0;
}

B. Paranoid String

题意

? 给你一个 0 , 1 0,1 0,1串,你有两种操作, 1 1 1. 选择相邻 01 01 01编程一个 1 1 1 2 2 2. 选择相邻的 10 10 10变成一个 0 0 0,问你有多少个数对 ( l , r ) (l,r) (l,r)使得 [ s l , s r ] [s_l,s_r] [sl?,sr?]通过若干次操作后变成长度为 1 1 1的串。

思路

  • 枚举、思维

? 对于统计多少个数对我们考虑枚举每个 i i i i i i前面哪些位置可以构成数对

  1. 首先 i i i和它自己可以构成数对

  2. 如果 i i i i ? 1 i-1 i?1一样的话无论前面选到哪怎么操作最后至少会留下两个相邻的一样的数,因此没有贡献

  3. 如果 i i i i ? 1 i-1 i?1不一样的话, i i i和前面任意一个位置构成点对都合法,因为前面的所有情况只会分成两类 :

    1. i i i之前顺利合并了由于 i i i i ? 1 i-1 i?1不同因此它们也可以合并

    2. i i i之前无法顺利合并则意味着剩下的均是 i ? 1 i-1 i?1这个字符, i i i和它们依次合并即可

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        string str; cin >> str;
        LL res = n;
        for (int i = 1; i < str.size(); i ++)
            if (str[i] != str[i - 1])
                res += i;
        
        cout << res << '\n';
    }
    return 0;
}

C. Directional Increase

题意

你有一个长度为 n n n的全零数组 a a a,一开始你在 1 1 1,每次你可以进行以下操作的一种:

  1. 从当前挪到前一个位置当前位置值减一,如果当前不在 1 1 1
  2. 从当前挪到后一个位置当前值加一,如果当前不在 n n n

给你一个数组 b b b,问你能否通过若干次操作将 a a a变成 b b b并且你最后位于 1 1 1这个位置

思路

  • 思维

? 首先观察一下性质,由于右移加一左移减一因此 b b b数组和应该为 0 0 0,先右移再左移,也就是先加再减,因此 b b b数组前缀和的每个位置应该 ≥ 0 \ge 0 0,然后到右边某个位置之后我们就要往回走了这个时候前缀和一定是 0 0 0(整体只有左右操作因此是 0 0 0),并且由于回去了所以后面的位置应该都是零。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define int long long
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
signed main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        for (int i = 1; i <= n; i ++) cin >> a[i];        
        bool ok = true, ok0 = false;
        int s = 0;
        for (int i = 1; i <= n; i ++) {
            s += a[i];
            if (ok0 && s != 0) ok = false;
            if (s < 0) ok = false;
            if (s == 0) ok0 = true;
        }
        if (s != 0) ok = false;
        cout << (ok ? "YES" : "NO") << '\n';
    }
    return 0;
}
  • 模拟

? 没抓住关键点这些性质的话,我们也可以模拟来做,首先右边最后一个不为 0 0 0的位置 r r r一定是我们往左走的点,并且 [ 2 , r ] [2,r] [2,r]最后都会往左走一次,因此我们统一 + 1 +1 +1将操作抵消,剩下的就之后相邻两项之间移动所产生的代价了。从 r ? 1 r-1 r?1往前枚举每一个位置,对于当前位置由于他会往右走一次 + 1 +1 +1,因此给他 ? 1 -1 ?1,这样就只剩下反复横跳的次数了,每横跳一次相当于它加一右边减一,因此我们将当前位置加上前一个位置的值相当于把反复横跳的代价抵消掉了,由于再往前走的所有的位置的操作只会让当前位置减所以当当前位置操作完的值应该 ≥ 0 \ge 0 0,判断每一个点即可。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define int long long
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
signed main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        int s = 0;
        for (int i = 1; i <= n; i ++) cin >> a[i], s += a[i];
                       
        int r = 1;
    
        for (int i = n; i; i --) 
            if (a[i] != 0) {
                r = i;
                break;
            }
        for (int i = 2; i <= r; i ++) a[i] ++;
        int v = a[r];
        bool ok = true;
        if (v > 0) ok = false;
        for (int i = r - 1; i >= 1; i --) {
            a[i] --;
            a[i] += v;
            v = a[i];
            if (v > 0) {
                ok = false;
                break;
            }
        }
        if (v != 0) ok = false;
       
        cout << (ok ? "YES" : "NO") << '\n';
    }
    return 0;
}

D. Fake Plastic Trees

题意

? 给你一颗有根树根为 1 1 1,每个点的点权 a a a初始为 0 0 0,对于每个点最终取值应该 [ l a i , r a i ] [l_{a_i},r_{a_i}] [lai??,rai??],每次你可以选择一个点 v v v,并且将 1 → v 1\to v 1v的最短路径上加上一个非递减序列,问你最少需要多少次操作让每个 a i a_i ai?均落在对应的取值区间内。

思路

  • 贪心, d p dp dp

? 首先对于每个点我们来看如何让他落在对应的取值区间内,1. 它的在消灭以他为根的子树的所有结点的时候顺带将他消灭了 2. 我们操作一次将他消灭。

? 因此我们从根开始搜,对于每个结点统计出以他为根的子树最多可以消灭多少数,对于某个结点 u u u,将它的所有子节点最多能够消除的数相加为 s s s,如果 s ≥ l u s\ge l_u slu?则说明在消灭它的子树的时候可以可以顺带将他消灭到,贪心来看能不操作就不操作,因此以 u u u为根的子树最多能消灭的数即 s s s,如果 s < l u s<l_u s<lu?则说明需要操作一次,由于它越大它的父节点就越可能被抵消掉因此让 s = r u s=r_u s=ru?并且操作加一即可,回溯的时候统计操作数即可

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define int long long
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
PII a[N];
int res;
int dfs(int u, int fa) {
    int s = 0;
    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if (j == fa) continue ;        
        s += dfs(j, u);
    }
    if (s >= a[u].x) return min(s, a[u].y);
    else {
        res ++;
        return a[u].y;
    }  
}
signed main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        for (int i = 1; i <= n; i ++) h[i] = - 1;
        for (int i = 2; i <= n; i ++) {
            int x; cin >> x;
            add(x, i), add(i, x);
        }
        for (int i = 1; i <= n; i ++) cin >> a[i].x >> a[i].y;
        res = 0;
        dfs(1, 0);

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

E. Keshi in Search of AmShZ

题意

给你一个 n n n个点 m m m条边的有向图,一开始你在 1 1 1号点,每次你有两种操作:

  1. 任选一条边将他删除掉
  2. 走一步,但是是随机走无法规定方向

问你从 1 1 1号点走到 n n n号点最坏情况下通过最优操作需要操作多少次

思路

  • d p dp dp,最短路

? 首先我们设 d i s t u dist_u distu?: 从点 u u u走到点 n n n的最少操作次数,假设对于某个点 u u u,它的直接后继有 a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1?,a2?,...,ak?这些点且 d i s t a 1 > d i s t a 2 > . . . > d i s t a k dist_{a_1}>dist_{a_2}>...>dist_{a_k} dista1??>dista2??>...>distak??,那么对于 u u u来说选择 a k a_k ak?的的花费就是 k k k,因为要将 a [ 1 , k ? 1 ] a_{[1,k-1]} a[1,k?1]?都删掉并且走到 a k a_k ak?,对于选择 a 2 a_2 a2?的花费是 2 2 2,需要将 a 1 a_1 a1?删掉并且走到 a 2 a_2 a2?,由于 a [ 3 , k ] a_{[3,k]} a[3,k]?的权值都小于 a 2 a_2 a2?因此最坏情况下考虑只会走到 a 2 a_2 a2?,因此对于某个点 u u u的最优解就是 d i s t u = m i n ( d i s t u , d i s t k + 大 于 d i s t k 的 后 记 结 点 数 ) k ∈ ( 后 继 节 点 ) dist_u=min(dist_u,dist_k+大于dist_k的后记结点数) k\in(后继节点) distu?=min(distu?,distk?+distk?)k()

? 显然我们可以反向建图倒着 d p dp dp,设 c n t u : 为 u 的 后 继 结 点 个 数 cnt_u: 为u的后继结点个数 cntu?:u,之后我们可以跑一个 d i j k s t r a dijkstra dijkstra,,由于每次都是取出当前 d i s t dist dist最小的数,假设取的为 u u u则对于它的后继 v v v如果更新完就要将 c n t [ v ] cnt[v] cnt[v]–,这样每次更新的时候 c n t cnt cnt中寸的恰好是大于某个 d i s t dist dist的后继节点的个数,因此跑一遍 d i j k s t r a dijkstra dijkstra的过程就做完了我们的 d p dp dp

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N], cnt[N];
int dist[N];
bool st[N];
void dijkstra() {
    priority_queue<PII, vector<PII>, greater<PII>> pq;
    memset(dist, 0x3f, sizeof dist);
    dist[n] = 0;
    pq.push({0, n});
    while (pq.size()) {
        auto t = pq.top(); pq.pop();
        if (st[t.y]) continue ;
        st[t.y] = true;
        for (int i = h[t.y]; ~i; i = ne[i]) {
            int j = e[i];
            dist[j] = min(dist[j], t.x + cnt[j]);
            cnt[j] --;
            pq.push({dist[j], j});
        }
    }
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 1; i <= m; i ++) {
        int x, y; cin >> x >> y;
        add(y, x), cnt[x] ++;
    }
    dijkstra();
    cout << dist[1] << '\n';
    return 0;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-06-23 00:59:44  更:2022-06-23 01:00:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 1:29:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码