2020 ICPC 澳门(G,J,I)详解

链接:The 2020 ICPC Asia Macau Regional Contest

G Game on Sequence

题意

给定长度为 n n n 数组 a i a_i ai,A与G博弈,G先手,给定初始位置 k k k,若当前在 i i i 点转移到 j j j,满足 i < j i < j i<j,并且 a i , a j a_i, a_j ai,aj 二进制数位最多只有一位不同,谁不能移动了就输了。现在有两个操作,操作1:在数组末尾增加一个数 k k k. 操作2:询问从 k k k 出发二者谁赢。

思路

乍一看每次新增一个数似乎都要将前面的都更新一遍时间复杂度爆炸,但是对于博弈的这种题我们需要发现一些性质来入手。
我们知道平等组合游戏中,若当前点能转移到必败点,则当前点是必胜点。
在这里插入图片描述

s g i = 0 / 1 sg_i = 0/1 sgi=0/1 分别代表从该点出发先手必败/必胜。

我们考虑以下情况若存在两个位置 a i = a j ( i < j ) a_i = a_j (i < j) ai=aj(i<j) a j a_j aj s g j sg_j sgj 值分类讨论:
s j = 1 s_j = 1 sj=1 则说明 a j a_j aj 后存在一个可转移的点 k k k s g k = 0 sg_k = 0 sgk=0,那么 a i a_i ai 同样可以转移到 k k k,所以 s g i = 1 sg_i = 1 sgi=1.
s j = 0 s_j = 0 sj=0 a i a_i ai 可以直接转移 j j j 点, s g i = 1 sg_i = 1 sgi=1.
所以若 a i a_i ai 后存在相同的值,则 a i a_i ai 必胜。

所以每次增加一个数从后往前更新,只需要更新能转移的数的最后一个位置就可以了,总共只有 0 ∼ 255 0\sim255 0255 种数。

具体见代码有详细注释。

代码

#include <bits/stdc++.h>
using namespace std;const int N = 5e5 + 10, M = 300;int sg[N], a[N];int last[M];void update(){vector<int> A;for(int i = 0; i <= 255; i ++){if(last[i]) A.push_back(last[i]);}sort(A.begin(), A.end()); // 排序,因为一定要从后向前更新for(int i = A.size() - 1; i >= 0; i --){sg[A[i]] = 0; // 先赋值为 0for(int j = 0; j < 8 && !sg[A[i]]; j ++){ // 重新将其更新int bi = (a[A[i]] ^ (1 << j));if(last[bi] > A[i] && !sg[last[bi]]) sg[A[i]] = 1;}}
}
int main(){ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);int n, m;cin >> n >> m;for(int i = 1; i <= n; i ++){cin >> a[i];if(last[a[i]]) sg[last[a[i]]] = 1; // 后面有相同值,该点必胜last[a[i]] = i;}for(int i = n; i >= 1; i --){ // 从后向前转移if(sg[i]) continue ; // 只从必败点转移for(int j = 0; j < 8; j ++){ // 枚举可以转移到此的数,且只更新最后一个该数int bi = (a[i] ^ (1 << j)); // 改变j数位上的数if(last[bi]) sg[last[bi]] = 1; // 有必败点则自己必胜}}for(int i = 1; i <= m; i ++){int op, k;cin >> op >> k;if(op == 1){if(last[k]) sg[last[k]] = 1; // 与自己相同的必胜last[k] = ++ n;a[n] = k;update(); // 更新所有值最后点的sg值}else{// printf("sg[%d] = %d\n",k, sg[k]);cout << (sg[k] == 1?"Grammy":"Alice") << "\n";}}return 0;
}

J Jewel Grab

题意

给定 n n n 个物品有颜色 c i c_i ci 和 价值 v i v_i vi,两个操作。操作1:将 x x x 号物品颜色价值改为 c j , v j c_j,v_j cj,vj. 操作2:从 s s s 位置开始,不允许拿走同色的物品,但可以跳过 k k k 次选择不拿,问最多能拿价值多少的物品。(拿走物品后,下一次操作前就会原样回复,但修改不会)。

思路

我们可以随便用线段树/树状数组维护价值。考虑主要的颜色问题。

对于和区间数字种类有关的问题,我们考虑维护一个 l a s t i last_i lasti,代表该位置前一个同色的物品的位置。用线段树维护 l a s t i last_i lasti 的区间最大值,因为 k k k 很小,第一次我们都在线段树区间 [ s , n ] [s,n] [s,n] 中二分找 l a s t i ≥ s last_i \geq s lastis 的最小的位置,每次询问过后得到 i d x idx idx(这就是我们第一个要跳过的颜色),下次询问的时候就扩展至在区间 [ i d x + 1 , n ] [idx + 1, n] [idx+1,n] 继续询问。 这样就能找到所有重复的颜色,减去同色的较小值,保留最大值,最后再用树状数组求一个区间求和即可。

具体见代码。

代码

/* 
对于找相同元素的问题,可以考虑维护last:上一个相同元素的位置
线段树维护最大的last,线段树上二分逐一找到10个重复元素*/
#include <bits/stdc++.h>
using namespace std;#define ls p << 1
#define rs p << 1 | 1
#define ll long longconst int N = 2e5 + 10, inf = 1e9;int n, m;
ll rt[N]; // 树状数组维护区间求和单点修改
int lowbit(int x){ return x & -x; }
void update(int r, int k){for(int i = r; i <= n; i += lowbit(i)) rt[i] += k;
}
ll get_sum(int l, int r){ll ans = 0;for(int i = r; i; i -= lowbit(i)) ans += rt[i];for(int i = l - 1; i; i -= lowbit(i)) ans -= rt[i];return ans;
}set<int> s[N]; // si:维护颜色i的下标
struct seg_tree{int l, r, max_last;
}tr[N * 4];void build(int p, int l, int r){tr[p] = {l, r, 0};if(l == r) return ;int mid = (l + r) >> 1;build(ls, l, mid); build(rs, mid + 1, r);
}void pushup(int p){tr[p].max_last = max(tr[ls].max_last, tr[rs].max_last);
}
void update(int p, int loc, int k){if(tr[p].l == tr[p].r){tr[p].max_last = k;return ;}int mid = tr[ls].r;if(loc <= mid) update(ls, loc, k);else update(rs, loc, k);pushup(p);
}int query(int p, int k, int l, int r){ // 线段树上二分if(tr[p].max_last < k) return inf;if(tr[p].l == tr[p].r) return tr[p].l;if(l <= tr[p].l && tr[p].r <= r){if(tr[ls].max_last >= k) return query(ls, k, l, r);else return query(rs, k, l, r);}int mid = tr[ls].r, ans = inf;if(l <= mid) ans = query(ls, k, l, r);if(r > mid && ans == inf) ans = query(rs, k, l, r);return ans;
}int c[N], v[N], last[N], nex[N], last_c[N]; // lasti:前一个同色的位置 nexi:后一个同色的位置void solve(){int si, k;cin >> si >> k;vector<int> ci;ll sub = 0;int idx = si;// 此处last_c[i]: 中存的是需要跳过的颜色的最大价值for(int i = 1; i <= k && idx < n; i ++){idx = query(1, si, idx + 1, n);if(idx == inf) break;if(!last_c[c[idx]]) last_c[c[idx]] = v[last[idx]]; // 之前没有跳过该颜色,记录if(last_c[c[idx]] < v[idx]){ // 减去除了最大价值的同色的价值sub -= last_c[c[idx]];last_c[c[idx]] = v[idx];}else sub -= v[idx];ci.push_back(c[idx]);}for(auto x : ci) last_c[x] = 0;idx = (idx + 1 <= n) ? query(1, si, idx + 1, n) : inf; // 找到最近的不能跳过的点idx = min(idx - 1, n); cout << get_sum(si, idx) + sub << "\n";
}void update(){int x, ci, vi;cin >> x >> ci >> vi;update(x, vi - v[x]); v[x] = vi;if(ci == c[x]) return  ;if(last[x] && nex[x]){update(1, nex[x], last[x]); last[nex[x]] = last[x];nex[last[x]] = nex[x];}else if(last[x]) nex[last[x]] = 0;else if(nex[x]){update(1, nex[x], 0);last[nex[x]] = 0;}s[c[x]].erase(x);auto it = s[ci].lower_bound(x);last[x] = nex[x] = 0;if(it != s[ci].end()){int nx = *it;update(1, nx, x);last[nx] = x;nex[x] = nx;}if(it != s[ci].begin()){int la = *prev(it);update(1, x, la);last[x] = la;nex[la] = x;}else update(1, x, 0);s[ci].insert(x);c[x] = ci;
}
int main(){ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin >> n >> m;build(1, 1, n);for(int i = 1; i <= n; i ++){ // 此处last_ci:颜色i的最后一个位置cin >> c[i] >> v[i];update(i, v[i]);int idx = last_c[c[i]];if(idx){last[i] = idx;nex[idx] = i;}last_c[c[i]] = i;if(last[i]) update(1, i, last[i]);s[c[i]].insert(i);}for(int i = 1; i <= n; i ++) last_c[i] = 0;for(int i = 1; i <= m; i ++){int op;cin >> op;if(op == 1) update();else solve();}return 0;
}

C Club Assignment

用的很麻烦的思路,以及臭长的代码,明天再补思路和注释,写吐我了。

题意

思路

代码

#include <bits/stdc++.h>
using namespace std;typedef pair<int, int> pii;
const int N = 1e5 + 10, inf = (1 << 30);struct DSU {std::vector<int> f;DSU() {}DSU(int maxn) {init(maxn);}void init(int maxn) {f.resize(++ maxn); // 重构容器大小到 nstd::iota(f.begin(), f.end(), 0); // 批量递增赋值}int find(int x) {while (x != f[x]) {x = f[x] = f[f[x]];}return x;}bool same(int x, int y) {return find(x) == find(y);}bool merge(int x, int y) {x = find(x);y = find(y);if (x == y) {return false;}f[y] = x;return true;}
};int son[N * 30][2], siz[N * 30], id[N * 30], tot;
void insert(int x, int ID){int p = 0;for(int i = 29; i >= 0; i --){int u = x >> i & 1;if(!son[p][u]) son[p][u] = ++ tot;p = son[p][u];siz[p] ++;}id[p] = ID;
}int check(int x){int p = 0;for(int i = 29; i >= 0; i --){int u = x >> i & 1;if(!son[p][u]) return 0;p = son[p][u];}return siz[p];
}pii get_min(int x, int s, int p){int ans = 0;for(int i = s; i >= 0; i --){int u = x >> i & 1;if(son[p][u]) p = son[p][u];else{p = son[p][!u];ans |= (1 << i);}}return {ans, id[p]};
}struct edge{int u, v, w;bool operator < (const edge& A)const{return w < A.w;}
};edge query(int p1, int x, int i, int p2, int s){ // 遍历子树小的一边edge res = {0, 0, inf};if(son[p1][0]) res = min(res, query(son[p1][0], x, i - 1, p2, s));if(son[p1][1]) res = min(res, query(son[p1][1], x + (1 << i - 1), i - 1, p2, s));if(res.w == inf){pii tmp = get_min(x, s, p2);res = {id[p1], tmp.second, tmp.first};}return res;
}edge e[N];
int cnt;
void dfs(int p, int i){ // 最小异或生成树if(son[p][0]) dfs(son[p][0], i - 1);if(son[p][1]) dfs(son[p][1], i - 1);if(son[p][0] && son[p][1]) {if(siz[son[p][0]] < siz[son[p][1]]){e[++ cnt] = query(son[p][0], 0, i - 1, son[p][1], i - 2);}else{e[++ cnt] = query(son[p][1], 1 << (i - 1), i - 1, son[p][0], i - 2);}e[cnt].w |= (1 << i - 1);}return ;
}DSU dsu;
int n, Enemy[N], c[N];
vector<int> g[N];void init(int op = 0){ // 清空for(int i = 0; i <= tot; i ++){son[i][0] = son[i][1] = id[i] = siz[i] = 0;}tot = 0;if(op) return ;dsu.init(n);for(int i = 1; i <= n; i ++){Enemy[i] = c[i] = 0;g[i].clear();}cnt = 0;
}void dfs(int u){ // 二分图染色for(auto v : g[u]){if(c[v]) continue ;c[v] = c[u] % 2 + 1;dfs(v);}
}
int a[N];
void solve(){cin >> n;init();for(int i = 1; i <= n; i ++){cin >> a[i]; insert(a[i], i);}dfs(0, 30);sort(e + 1, e + 1 + cnt);dsu.init(n);int ans = 0;for(int i = 1; i <= cnt; i ++){// auto [u, v, w] = e[i];int u = e[i].u, v = e[i].v, w = e[i].w;if(dsu.same(u, v)){// cout << "w = " << w << "\n";ans = w;break ;}g[u].push_back(v);g[v].push_back(u); // 建二分图if(!Enemy[u]) Enemy[u] = v;else dsu.merge(Enemy[u], v);if(!Enemy[v]) Enemy[v] = u;else dsu.merge(Enemy[v], u);}for(int i = 1; i <= n; i ++){if(Enemy[i]){if(!c[i]) c[i] = 1, dfs(i);}else c[i] = 1;}for(int i = 1; i <= n; i ++){if(!c[i]) c[i] = 1;}if(!ans){init(1);ans = inf;for(int i = 1; i <= n && ans; i ++){if(c[i] == 1){int sum = check(a[i]);if(sum >= 1){c[i] = 2;continue ;}if(!sum && tot){ans = min(ans, get_min(a[i], 29, 0).first);}insert(a[i], i);}}init(1);for(int i = 1; i <= n && ans; i ++){if(c[i] == 2){int sum = check(a[i]);if(sum >= 1) ans = 0;if(!sum && tot){ans = min(ans, get_min(a[i], 29, 0).first);}insert(a[i], i); }}}cout << ans << "\n";for(int i = 1; i <= n; i ++) cout << c[i];cout << "\n";
}int main(){ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);int t;cin >> t;while(t --){solve();}return 0;
}

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

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

相关文章

【编程语言发展史】SQL的发展历史

目录 目录 SQL概述 SQL发展历史 SQL特点 SQL基本语句 SQL是结构化查询语言(Structure Query Language)的缩写&#xff0c;它是使用关系模型的数据库应用语言&#xff0c;由IBM在70年代开发出来&#xff0c;作为IBM关系数据库原型System R的原型关系语言&#xff0c;实现了…

uni-app使用echarts数据不更新

问题描述 uni-app使用echarts图表&#xff0c;接口请求数据更新后&#xff0c;图表不刷新。 解决方案 更新option前先调用clear() clear()&#xff1a;清空当前实例&#xff0c;会移除实例中所有的组件和图表。 initChart() {this.gaugeChart echarts.init(document.getE…

安卓常见设计模式5------桥接模式(Kotlin版)

1. W1 是什么&#xff0c;什么是桥接模式&#xff1f; 桥接模式是一种结构性模式。 桥接模式旨在将抽象与实现解耦&#xff0c;使它们可以独立地变化。可以这么理解&#xff0c;面向对象编程是单继承多实现的&#xff0c;如果我们有一个可扩展类&#xff0c;和多个相关的可扩展…

农业4.0中麦田的精确杂草检测:实现技术、方法和研究挑战的综述

Precision weed detection in wheat fields for agriculture 4.0: A survey of enabling technologies, methods, and research challenges 摘要1、引言2、相关工作3、麦田常见杂草的种类及分布特征3.1 天然麦田中常见的杂草3.2 杂草分布格局4、先进的麦田杂草检测技术4.1 光谱…

MySQL -- 用户管理

MySQL – 用户管理 文章目录 MySQL -- 用户管理一、用户1.用户信息2.创建用户3.删除用户4.远端登录MySQL5.修改用户密码6.数据库的权限 一、用户 1.用户信息 MySQL中的用户&#xff0c;都存储在系统数据库mysql的user表中&#xff1a; host&#xff1a; 表示这个用户可以从…

关于Alibaba Cloud Toolkit 下载配置以及后端自动部署

idea中File-Settings-Plugins 搜索Alibaba Cloud Toolkit点击下载&#xff0c;下载完成重启 1、点击 Tools-Alibaba Cloud-Deploy to Host 部署到主机 2、配置服务器ip、jar包启动命令、服务器jar存放位置 3、设置服务器ip用户名密码&#xff0c;点击测试连接情况 4、配置脚本…

微信支付测试用例设计怎么设计?

功能测试用例&#xff1a; 测试支付流程是否正常&#xff0c;包括选择支付方式&#xff0c;输入金额&#xff0c;确认支付&#xff0c;输入密码&#xff0c;支付成功等步骤 测试不同的支付方式&#xff0c;如微信零钱&#xff0c;银行卡&#xff0c;信用卡等 测试不同的支付场…

写给新用户-Mac软件指南篇:让你的Mac更好用

用了macOS也有小四年了&#xff0c;今天打算分享一下Mac上的常用软件&#xff0c;说不上精通&#xff0c;但也算是有一些心得体会。平时也会定期对软件做整理&#xff0c;所以有了这篇文章。如果能帮到刚刚接触macOS或正在寻觅软件的你&#xff0c;那当然再好不过了。 软件推荐…

《第三期(先导课)》之《Python 开发环境搭建》

文章目录 《第 1 节 初始Python》《第 6 节 pip包管理工具》 《第 1 节 初始Python》 。。。 《第 6 节 pip包管理工具》 pip是Python的包管理工具,用于安装、升级和管理Python包。 pip是Python标准库之外的一个第三方工具,可以从Python Package Index(PyPI)下载和安装各种P…

自动化实战 - 测试个人博客系统

前言 本篇使用Selenium3Junit5对个人博客进行自动化测试&#xff0c;如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流&#xff0c;共同进步&#xff01; 文章目录 前言一.web自动化测试用例二.测试准备1.注册界面自动化测试测试过程中遇到的Bug: 2.登录界面自动…

Naocs配置中心配置映射List、Map、Map嵌套List等方式

一、配置映射List 1、常规逐个配置方式,示例如下: 代码: @Data @Configuration @ConfigurationProperties(prefix = "list-json-str") public class ConfListByJsonStr implements Serializable, InitializingBean {@ApiModelProperty("映射结果集")…

React 测试笔记 03 - 测试 Redux 中 Reducer 状态变化

React 测试笔记 03 - 测试 Redux 中 Reducer 状态变化 这段时间都在重构代码&#xff0c;把本来奇奇怪怪(singleton)的实现改成用 redux 的实现&#xff0c;然后就突然想到……即然 redux 的改变不涉及到 UI 的改变&#xff0c;那么是不是说可以单独写 redux 的测试……&#…

【遍历二叉树的非递归算法,二叉树的层次遍历】

文章目录 遍历二叉树的非递归算法二叉树的层次遍历 遍历二叉树的非递归算法 先序遍历序列建立二叉树的二叉链表 中序遍历非递归算法 二叉树中序遍历的非递归算法的关键&#xff1a;在中序遍历过某个结点的整个左子树后&#xff0c;如何找到该结点的根以及右子树。 基本思想&a…

Mabitys总结

一、ORM ORM(Object/Relation Mapping)&#xff0c;中文名称&#xff1a;对象/关系 映射。是一种解决数据库发展和面向对象编程语言发展不匹配问题而出现的技术。 使用JDBC技术时&#xff0c;手动实现ORM映射&#xff1a; 使用ORM时&#xff0c;自动关系映射&#xff1a; &am…

『MySQL快速上手』-⑥-表的约束

文章目录 1.空属性2.默认值3.列描述4.zerofill5.主键6.自增长7.唯一键8.外键9.综合案例真正约束字段的是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合法性,从业务逻辑角度保证数据的正确性。 1.空属性 数据库默认字段基本都是字段为空,但是…

【gltf-pipeline】安装gltf-pipeline 进行文件格式转换

问题 想使用gltf-pipeline进行gltf和glb格式转换。简单记录一下安装过程。 解决 1、安装Node.js Node.js下载路径&#xff1a;https://nodejs.org/en 建议默认设置安装。 添加系统环境变量&#xff1a; 测试安装是否成功&#xff1a; 在cmd.exe中运行&#xff1a; no…

基于ssm的大学生社团管理系统

基于ssm的大学生社团管理系统 摘要 基于SSM的大学生社团管理系统是一个全面、高效的社团管理平台&#xff0c;旨在帮助大学生和社团管理员更方便、更快捷地进行社团活动的组织和管理。该系统基于Spring、SpringMVC和MyBatis&#xff08;简称SSM&#xff09;开发&#xff0c;这三…

任务管理器的正确使用教程

快捷键 Ctrlshiftesc&#xff1a;进入任务管理器 我以Win11举例 如何给XX排序 给XX排序&#xff0c;点击空白处可以选择某项降序排列&#xff08;可以找到最占用某项资料的程序&#xff09;&#xff0c;再点击空白处可以选择某项升序排列 文件正在使用&#xff0c;如何解决 …

Java——Stream流的peek方法

Java Stream中的peek()方法也是用于查看每个元素&#xff0c;但不改变流的操作的方法。它接收一个Consumer类型的参数&#xff0c;该参数用于针对每个元素执行副作用操作。该方法返回一个与原始流相同的新流&#xff0c;因此可以进行链式操作。 使用peek()方法可以方便地在流处…

​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第1章-绪论-思维导图】 课本里章节里所有蓝色字体的思维导图