atcoder abc370(dp,基环树/森林,倍增)

news2024/11/26 10:24:26

代码:

#include <bits/stdc++.h>

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;
    if(a == 1 && b == 0) cout << "Yes" << endl;
    else if(a == 0 && b == 1) cout << "No" << endl;
    else cout << "Invalid" << endl;
    return 0;
}

B - binary aichemy

代码: 

#include <bits/stdc++.h>

using namespace std;

int main() {
    int n;
    cin >> n;
    vector<vector<int>> a((n + 1), vector<int>(n + 1));
    for(int i = 1; i <= n; i ++ ) {
        for(int j = 1; j <= i; j ++ ) {
            cin >> a[i][j];
        }
    }

    int val = 1;
    for(int i = 1; i <= n; i ++ ) {
        if(val >= i) val = a[val][i];
        else val = a[i][val];
    }
    cout << val;
    return 0;
}

C - word ladder

问题:

思路:先从前往后把字典序变小的字符改变,再从后向前把字典序变大的字符改变,由于数据范围很小,两个while即可

代码:

#include <bits/stdc++.h>

using namespace std;

string a, b;

bool cmp(int x, int y) {
    if(x < y) return b[x] < a[x];
    if(x > y) return b[y] < a[y];
}

int main() {
    cin >> a >> b;
    a = a;
    b = b;
    int n = a.length();
    vector<string> ans;
    bool flag = true;
    while(flag) {
        flag = false;
        for(int i = 0; i < n; i ++ ) {
            if(a[i] > b[i]) {
                flag = true;
                a[i] = b[i];
                ans.push_back(a);
            }
        }
    }
    
    flag = true;
    while(flag) {
        flag = false;
        for(int i = n - 1; i >= 0; i -- ) {
            if(a[i] != b[i]) {
                flag = true;
                a[i] = b[i];
                ans.push_back(a);
            }
        }
    }
    
    cout << ans.size() << endl;
    for(auto t: ans) cout << t << endl;
    return 0;
}

D - Cross Explosion

问题:

思路:用vector<set<int>> 存储每一行/列中没有被爆破掉的点,在查询时用二分查询第一个大于和第一个小于被查询的点

代码:runtime error中

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int main() {
    int n, m, q;
    cin >> n >> m >> q;
    
    vector<set<int>> row(n + 2), col(m + 2);
    vector<vector<bool>> st((n + 1), vector<bool>(m + 1));
    for(int i = 0; i <= n; i ++ ) {
        for(int j = 0; j <= m; j ++ ) {
            row[i].insert(j);
            col[j].insert(i);
        }
    }

    ll ans = 0;
    while(q -- ) {
        int a, b;
        cin >> a >> b;
        if(!st[a][b]) {
            ans ++;
            st[a][b] = true;
            row[a].erase(b);
            col[b].erase(a);
        } else {
            auto it = row[a].lower_bound(b);
            if(it != row[a].end()) {
                ans ++;
                row[a].erase(*it);
                col[*it].erase(a);
                st[a][*it] = true;
            }

            it --;
            if(it != row[a].begin()) {
                ans ++;
                row[a].erase(*it);
                col[*it].erase(a);
                st[a][*it] = true;
            }

            it = col[b].lower_bound(a);
            if(it != col[b].end()) {
                ans ++;
                col[b].erase(*it);
                row[*it].erase(b);
                st[*it][b] = true;
            }

            it --;
            if(it != col[b].begin()) {
                ans ++;
                col[b].erase(*it);
                row[*it].erase(b);
                st[*it][b] = true;
            }
        }
    }
    cout << n * m - ans;
    return 0;
}

E avoid k partition

问题:

思路:动态规划

首先很容易想到两重循环动态规划的写法  

for(int i = 1; i <= n; i ++ ) {
        for(int j = 0; j < i; j ++ ) {
            if(a[i] - a[j] != k) dp[i] += dp[j];
        }
}

设dp[i]表示以i结尾的各子段和不等于k的划分方法的集合

dp[0]初始化为1,以后的所有状态都由dp[0]转移, 对于所有小于i的j,如果j,i的子段和不等于k,那么对于dp[i]就可以加上dp[j]

注意到第二层循环实际上是所有满足条件的dp[j](j < i)的和,因此优化在这一层循环上做文章

如果不考虑分段的条件,只考虑分段的数量,那么dp[i]实际上就等于dp[i - 1], 再用上一轮所有合法解,减去这一轮判掉的所有不合法解

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll mod = 998244353;

ll qmi(ll a, ll b) {
    ll res = 1;
    while(b) {
        if(b & 1) (res *= a) %= mod;
        (a *= a) %= mod;
        b >>= 1;
    }
    return res;
}

int main() {
    ll n, k;
    cin >> n >> k;
    vector<ll> a(n + 1);
    vector<ll> dp(n + 1);
    for(int i = 1; i <= n; i ++ ) {
        cin >> a[i];
        a[i] += a[i - 1];
    }
    
    dp[0] = 1;
    /*for(int i = 1; i <= n; i ++ ) {
        for(int j = 0; j < i; j ++ ) {
            if(a[i] - a[j] != k) dp[i] += dp[j];
        }
    }*/
    map<ll, ll> ma;
    ma[0] ++;
    ll tot = 1;
    //tot 是上一轮循环中所有不等于k的分段方法和
    for(int i = 1; i <= n; i ++ ) {
        //ll tot = qmi(2, i - 1);
        (dp[i] += tot - ma[a[i] - k] + mod) %= mod;//从所有合法方案中减去不合法方案
        (ma[a[i]] += dp[i]) %= mod;//维护的是所有dp[i](i < j)的和,且前缀和为a[i]
        (tot += dp[i]) %= mod;//维护的是所有dp[i](i < j)的和,是目前为止对于j来说的所有合法方案
    }
    cout << dp[n];
    return 0;
}

这里的tot与ma存的都是上一轮结束后的状态,与背包问题一维优化思想一致.

F cake division

思路:这一题乍一看很简单,结果求最小值的max,直接二分答案,重点是check函数怎么写

当我们二分出一个值mid之后,我们划分的每一段都不能小于这个值(除非该值本身非法),又由于划分的数量有要求,即k段,因此我们应该贪心的使我们划分的每一段在不小于mid的前提下最小,这样处理完每个点之后实际上会形成一张图,

(当然可能是多张图。另外,假设i 点最后处理到的点是j点,由于下一次划分要从j + 1开始,因此我们可以直接设置i 点应该跳到j + 1,以此简化处理)

因此,由于必须划分k段,我们可以在判断最后一段合法之后把剩余部分接到最后一段上,这样不会影响最小值(我们只需要知道这一点,没有必要在写代码时接上最后一段)。同时,由于处理的蛋糕是个环,因此要破环为链(acwing能量项链)。在check时,如何判断一个划分合法呢,首先为划分设置一个哨兵,将a[2 * n + 1](前缀和数组)设为1e18,然后当我们枚举划分起点,如果从某个点开始跳k步后仍然没有走出一个环的距离,即end pos <= i + n那么就认为这段划分合法, 在这里,枚举要枚举n个起点,每个起点都要走k步,时间复杂度是 nk显然超时,但是第二层循环可以用倍增优化(acwing祖孙询问),在这里的倍增容易出错的点就是上限设置以及循环顺序:
 

void init(ll m) {
    for(int i = 1; i <= 2 * n + 1; i ++ ) {
        for(int j = 0; j <= 19; j ++ ) {
            dp[i][j] = 2 * n + 1;
        }
    }
    
    for(int i = 1; i <= n * 2; i ++ ) {
        int l = i, r = 2 * n;
        while(l < r) {
            int mid = l + r >> 1;
            if(a[mid] - a[i - 1] >= m) r = mid;
            else l = mid + 1;
        }
        dp[i][0] = l + 1;
    }
        
    for(int j = 1; j <= 19; j ++ ) {
        for(int i = 1; i <= 2 * n + 1; i ++ ) {
            dp[i][j] = dp[dp[i][j - 1]][j - 1];
        }
    }
}

dp[0 ~ 2 * n + 1][0 ~ x] = 2 * n + 1这个初始化锁定了上限,2 * n + 1是上限的下一个点

于是时间复杂度被优化成了n logk

再看第二问,实际上就是在遍历倍增的同时,记录下哪个点的end pos > n + i

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const ll mod = 998244353;
const ll N = 2e5 + 10;

ll a[N * 2];
ll dp[N * 2][20];
ll n, k;

void getdp(ll m) {
    for(int i = 1; i <= 2 * n + 1; i ++ ) {
        for(int j = 0; j <= 19; j ++ ) {
            dp[i][j] = 2 * n + 1;
        }
    }
    
    for(int i = 1; i <= n * 2; i ++ ) {
        int l = i, r = 2 * n;
        while(l < r) {
            int mid = l + r >> 1;
            if(a[mid] - a[i - 1] >= m) r = mid;
            else l = mid + 1;
        }
        dp[i][0] = l + 1;
    }
        
    for(int j = 1; j <= 19; j ++ ) {
        for(int i = 1; i <= 2 * n + 1; i ++ ) {
            dp[i][j] = dp[dp[i][j - 1]][j - 1];
        }
    }
}


bool check(ll m) {
    getdp(m);
    for(int i = 1; i <= n; i ++ ) {
        int pos = i;
        for(int j = 0; j <= 19; j ++ ) {
            if(k >> j & 1) {
                pos = dp[pos][j];
            }                
        }
        if(pos <= n + i) return true;
    }
    return false;
}


int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin >> n >> k;
    a[2 * n + 1] = 1e18;
    ll minv = 1e18;
    for(int i = 1; i <= n; i ++ ) {
        cin >> a[i];
        minv = min(minv, a[i]);
    }
    for(int i = n + 1; i <= 2 * n; i ++ ) a[i] = a[i - n];
    for(int i = 1; i <= 2 * n; i ++ ) a[i] += a[i - 1];
    
    ll l = minv, r = a[2 * n];
    while(l < r) {
        ll mid = l + r + 1 >> 1;
        if(check(mid)) l = mid;
        else r = mid - 1;
    }
    cout << l << " ";
    
    getdp(l);
    ll ans = 0;
    for(int i = 1; i <= n; i ++ ) {
        int pos = i;
        for(int j = 0; j <= 19; j ++ ) {
            if(k >> j & 1) {
                pos = dp[pos][j];
            }                
        }
        if(pos > n + i) ans ++;
    }
    cout << ans;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2124699.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Centos】Centos系统换yum源

【Centos】Linux&#xff0c;Centos系统换yum源 1、备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak/etc/yum.repos.d/CentOS-Base.repo 是yum的配置文件 /etc/yum.repos.d/CentOS-Base.repo.bak 是我们备份的配置文件 2、下载yum源 这里…

这个开源的AI证件照项目又火了!有人靠它日入300+

今天在知识星球里看到一个球友靠一个AI证件照的项目赚到了第一桶金&#xff0c;看了看项目&#xff0c;还不错。整理下来分享给大家玩玩。 神器名叫HivisionIDPhotos&#xff0c;最近几天一直挂在GitHub热榜上&#xff0c;目前已狂揽3.2K星标。 除了能换背景&#xff0c;它还支…

你绝对想不到,ComfyUI竟然能这样转换线条图!

前言 使用ComfyUI将图像转为线条图&#xff1a;详细教程 在这个数码时代&#xff0c;图像处理技术已经像空气一样渗透进了我们的日常生活。今天&#xff0c;我想和大家分享一个既简单又高效的小妙招——用ComfyUI把图片变成线条图。 不管你是设计师、艺术家&#xff0c;还是…

深入掌握大模型精髓:《实战AI大模型》带你全面理解大模型开发!

今天&#xff0c;人工智能技术的快速发展和广泛应用已经引起了大众的关注和兴趣&#xff0c;它不仅成为技术发展的核心驱动力&#xff0c;更是推动着社会生活的全方位变革。特别是作为AI重要分支的深度学习&#xff0c;通过不断刷新的表现力已引领并定义了一场科技革命。大型深…

opencv羊群计数,动态目标检测跟踪

OpenCV&#xff08;开源计算机视觉库&#xff09;是一个功能强大的计算机视觉和图像处理库&#xff0c;广泛应用于各种视觉任务中&#xff0c;包括但不限于目标检测与跟踪。如果你正在考虑一个基于OpenCV的羊群计数项目&#xff0c;那么下面是对这样一个项目的概述&#xff1a;…

【动态规划】子序列问题二(数组中不连续的一段)

子序列问题二 1.最长定差子序列2.最长的斐波那契子序列的长度3.最长等差数列4.等差数列划分 II - 子序列 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&am…

易基因:Adv Sci:ACE等揭示产前不良环境暴露通过DNA羟甲基化变化介导子代自闭症|国人佳作

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 自闭症谱系障碍&#xff08;Autism spectrum disorder&#xff0c;ASD&#xff09;是一种神经发育障碍&#xff0c;以社交沟通障碍和刻板行为为主要特征。许多研究证明&#xff0c;妊娠期…

2024上半年上汽收入2770多亿,为啥没长城900多亿赚得多?

【科技明说 &#xff5c; 车评头条】 看了上汽集团和长城汽车两家的最新H1财报后&#xff0c;你是不是发现了点什么&#xff1f; 上汽集团披露2024年半年报。公司上半年实现合并营业收入2770.86亿元&#xff0c;同比下降12.43%&#xff1b;净利润66.28亿元&#xff0c;同比下…

vb.net发送邮件:如何高效地实现邮件发送?

vb.net发送邮件怎么配置服务器&#xff1f;怎么用vb.net发邮件&#xff1f; 如何高效地实现vb.net发送邮件&#xff0c;确保邮件能够快速、稳定地送达&#xff0c;是许多开发者面临的挑战。AokSend将深入探讨vb.net发送邮件的最佳实践&#xff0c;帮助您提升邮件发送的效率和可…

投屏开发调试技能-pcm数据转wav格式文件源码实战分享

背景 在学习投屏相关音视频开发时候&#xff0c;经常验证一些声音卡顿问题时候&#xff0c;需要对音频数据可能需要保存到本地&#xff0c;一般可能是pcm格式的数据&#xff0c;但是pcm格式的数据是不可以用音乐播放器直接进行播放&#xff0c;需要专门的工具&#xff0c;而且…

14种实际上有效的AI营销方法(专业推荐)

当有人提到人工智能时&#xff0c;你会感到头晕目眩吗&#xff1f;这是可以理解的。LinkedIn和Twitter&#xff08;好吧……现在叫X&#xff09;充斥着刚刚涌现的AI专家和科技达人们&#xff0c;他们在分享最新的27步算法攻略和自动化整个市场营销程序的操作。 这对大多数基层…

一款rust语言AI神器cursor在ubuntu环境下的安装启动教程

虽然cursor目前只支持英文但是它强大的代码联想能力以及问答能力&#xff0c;可以高效的提高编码效率。 如下步骤所有的前提是你的ubuntu上面已经安装了rust以及其必须的extensions。 1 下载 到官网https://www.cursor.com下载指定版本的软件。 下载到本地以后会生成如下软件…

如何通过网络找到自己想要的LabVIEW知识?

学习LabVIEW或其他编程技术时&#xff0c;无法依赖某一篇文章解决所有问题。重要的是通过多种途径获取灵感&#xff0c;并学会归纳总结&#xff0c;从而逐渐形成系统性的理解。这种持续学习和总结的过程是技术提升的基础。通过网络找到所需的LabVIEW知识可以通过以下几个步骤进…

WEB渗透权限维持篇-MSSQL后门

往期文章WEB渗透权限维持篇-DLL注入\劫持-CSDN博客 WEB渗透权限维持篇-CLR-Injection-CSDN博客 WEB渗透权限维持篇-计划任务-CSDN博客 WEB渗透权限维持篇-DLL注入-修改内存中的PE头-CSDN博客 WEB渗透权限维持篇-DLL注入-进程挖空(MitreT1055.012)-CSDN博客 WEB渗透权限维…

轻松上手LangChain:新手必读的入门指南

导语 在人工智能领域的不断发展中&#xff0c;语言模型扮演着重要的角色。特别是大型语言模型&#xff08;LLM&#xff09;&#xff0c;如ChatGPT&#xff0c;已经成为科技领域的热门话题&#xff0c;并受到广泛认可。在这个背景下&#xff0c;LangChain作为一个以LLM模型为核…

打造高效业务架构:价值流在企业转型中的应用指南

从流程到价值流的业务架构转型 随着企业面对数字化转型带来的激烈市场竞争&#xff0c;优化业务架构成为每个企业管理者必须面对的核心挑战。传统的业务流程优化方法往往难以应对复杂的客户需求和日益增加的业务复杂性。《价值流指南》由The Open Group发布的企业数字化转型专…

K-Means算法详解与实战应用.

在数据分析的众多工具中&#xff0c;K-Means聚类算法以其简单、直观和高效的特点&#xff0c;成为了探索数据集结构的常用方法。本文将带你深入了解K-Means算法的原理&#xff0c;并展示如何在实际项目中运用这一强大的聚类工具。 一 算法原理 K-Means是一种迭代聚类算法&…

共享旅游卡,客户旅游云南,真实反馈,全程无删减!

​这是团队伙伴袁总的客户&#xff0c;也是袁总的朋友&#xff0c;使用千益畅行旅游卡&#xff0c;亲身带家人去旅游云南后体验反馈。 从抗拒旅游卡&#xff0c;报付费团旅游&#xff0c;到了解旅游卡&#xff0c;使用旅游卡去体验&#xff0c;中途的担忧顾虑&#xff0c;到结…

计算机网络(五) —— 自定义协议简单网络程序

目录 一&#xff0c;关于“协议” 1.1 结构化数据 1.2 序列化和反序列化 二&#xff0c;网络版计算器实现准备 2.1 套用旧头文件 2.2 封装sock API 三&#xff0c;自定义协议 3.1 关于自定义协议 3.2 实现序列化和反序列化 3.3 测试 三&#xff0c;服务器实现 3.1…

【C++ 面试题】构造函数和析构函数你了解多少呢?

文章目录 1. 什么是构造函数和析构函数2. 构造函数和析构函数可以是虚函数吗3. 构造函数有哪几种4. 深拷贝和浅拷贝的区别 1. 什么是构造函数和析构函数 &#x1f427; 构造函数&#xff1a; 构造函数是在创建对象时自动调用的特殊成员函数。 目的&#xff1a;初始化对象的成…