Codeforces Round #649 (Div. 2)
Codeforces Round #649 (Div. 2) -- WKL
\(\mathcal{A}\)题: \(\mathrm{XXXXX}\)
Greedy
implementation
*1200
第一题,要求的是求一段子数组的区间和,要求该区间和不被\(x\)整除且长度尽可能长。
显然,对于这类题目可以想到以下几点:
- \(MOD\)的使用
- 贪心与构造
思路如下:定义数组为\(arr\)。我们首先看\(\sum arr\)是否符合要求,假如符合显然这个是最长的。假如不符合呢?我们只需要从两端找到第一个\(\mathrm{mod} x \not= 0\)的即可,然后从中选择更优的就解决了。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int f[N];
// 贪心,要最长, 左右减去就好了(T1一般不是贪心就是数学问题)
int main(){
int t; cin >> t;
while (t--){
int n, x;
cin >> n >> x;
int sum(0), l(-1), r(-1), ans(-1);
for (int i = 0; i < n; ++ i){
cin >> f[i];
sum += f[i];
if (f[i] % x && l == -1) l = i;
if (f[i] % x) r = i;
}
if (sum % x) cout << n << endl;
else if (l == -1 && r == -1) cout << -1 << endl;
else{
ans = max(n - l - 1, r);
cout << ans << endl;
}
}
return 0;
}
\(\mathcal{B}\)题: \(\mathrm{Most\; socially-distanced\; subsequence}\)
Greedy
two pointers
*1300
\(\mathcal{B}\) 题的话从题干中获取如下两点就好了:
- \[\begin{array}
x seq = \{s_1, s_2, \cdots, s_k\}\\
\mathrm{get}-\max\{\left|s_1 - s_2\right| + \left|s_2 - s_3\right| + \cdots + \left|s_{k-1} - s_{k}\right|\} \\
\mathrm{with-}\min\{len(seq)\}
\end{array}
\]
根据分析,当序列\(\{s_i, s_j, s_k\}\)单调时,\(|s_i - s_j| + |s_j - s_k| = |s_i - s_k|\) 。因此我们只需要在添加首尾两点之后找到极大极小值点就好了。就像化学里面能量计算一样
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int f[N];
// 找转折点 因为单调的话是没有用的,只有转折才有用(有点像化学里面的能量变化问题)
int main(){
int t; cin >> t;
while (t--){
vector<int> ans;
int n; cin >> n;
for (int i = 0; i < n; ++ i) cin >> f[i];
for (int i = 0; i < n; ++ i){
if (i == 0 || i == n -1 || ((f[i] < f[i - 1] && f[i] < f[i + 1]) || (f[i] > f[i - 1] && f[i] > f[i + 1])))
ans.push_back(f[i]);
}
cout << ans.size() << endl;
for (auto &x: ans) cout << x << " "; cout << endl;
}
}
\(\mathcal{C}\)题: \(\mathrm{Ehab\; and\; Prefix\; MEXs}\)
Greedy
constructive algorithms
*1600
需要注意的是
It's guaranteed that \(a_i\leq a_{i+1}\) for \(1\leq i\leq n\).
一共有更具MEX的定义可以获得两点信息:
- \(b_i\)首先应该填入\(1 \sim a_i - 1\)的数字,假如已经填满了,则填允许填的数字
- 实际上,作为升序序列,从\(0\)到\(\max{a_i}\),除了\(a_i\)自身外,其他的数字都应该出现。因此可以贪心去做,也可以维护一个链表去做。
首先是比赛中的方法,通过维护一个链表(链表中是还没有被添加的数字),每次要加入时判断还有不要小于\(a_i\)的数字未被添加:
- 假如只有一个,或没有则正常添加。
- 假如有多个,则说明无法成立。
#include<bits/stdc++.h>
using namespace std;
#define MP(x, y) make_pair(x, y)
#define fi first
#define se second
using PII = pair<int ,int>;
const int N = 1e5 + 50;
int c[N], arr[N]; // c[i] 代表i存在的个数,当大于0时说明不允许添加
int main(){
int n; cin >> n;
memset(c, 0, sizeof(c));
for (int i = 0; i < n; ++ i) cin >> arr[i], ++ c[arr[i]];
int pt(0), scan(0);
vector<int> ans;
vector<PII> que; // 模拟的链表, pt是指向头的指针,scan是用来添加那些已经填充满了的b_i
que.clear();
for (int i = 0; i <= n; ++ i) que.push_back(MP(i, 0)); // 初始化
for (int i = 0; i < n; ++ i){
int lim = arr[i] - 1;
if (que[pt].fi > lim){ // 假如当前链表头部的值大于lim,说明该加入的已经假如
while (c[scan] != 0 || que[scan].se == 1) ++ scan; // 往后面找一个值填进去
que[scan].se = 1;
ans.push_back(que[scan].fi);
while (que[pt].se == 1) ++ pt; // 更新point
-- c[arr[i]];
}else {
ans.push_back(que[pt].fi); // 说明不满足条件1,首先加一个进去再判断是否满足条件
que[pt].se = 1;
while (que[pt].se == 1) ++ pt;
-- c[arr[i]];
if (que[pt].fi > lim) continue;
else {
cout << -1 << endl;
return 0;
}
}
}
for (auto &x: ans) cout << x << " "; cout << endl;
return 0;
}
还有一种,便是题解所给的办法。通过贪心和构造快速解决。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 50;
int c[N], a[N], b[N]; // c[i] 代表i存在的个数,当大于0时说明不允许添加
int main(){
int n; cin >> n;
memset(b, -1, sizeof(b));
memset(c, 0, sizeof(c));
for (int i = 0; i < n ;++ i){
cin >> a[i];
if (i != 0 && a[i] != a[i - 1]){
b[i] = a[i - 1];
++ c[b[i]];
}
}
++ c[a[n - 1]];
int m = 0;
for (int i = 0; i < n; ++ i){
if (b[i] == -1){
while (c[m]) ++ m;
b[i] = m;
++ c[b[i]];
}
cout << b[i] << " ";
}
cout << endl;
return 0;
}
\(\mathcal{D}\)题: \(\mathrm{Ehab's\; Last\; Corollary}\)
dfs
graphs
trees
*2100
extra: 最小环
这题,看了半天才会写。这题要求的是对于给出一张 \(n\) 个点的无向连通图和一个常数 \(k\)。你需要解决以下任何一个问题中的一个:
- 找出一个大小为\(\lceil \frac{k}{2} \rceil\)的独立集。
- 找出一个大小不超过\(k\)的环。
独立集: 独立集是一个点的集合,满足其中任意两点之间在原图上没有边直接相连。
首先你得理解出题人的这句话, 才能方便的解决这个问题:
I have a proof that for any input you can always solve at least one of these problems, but it's left as an exercise for the reader.
就是,为什么一定能有一个解决办法呢?
我们从两种情况来看:
情况1: 假如所给的无向图根本没有环, 可以直接用黑白染色解决\(\mathcal{Q_1}\),即将更大的染色集输出即可。
情况2:假如存在环,也有两种情况:
- 环的长度\(L\)小于等于\(k\), 那好办直接输出就好了!
- 假如环的大小超过\(k\): 对于一个最小环,对于环上点\(i,j\)(假设这里边的权值都为\(1\))。设\(dist_{ij}\)为顺着环走的最短路径;\(dist^\prime_{ij}\)为\(i \rightarrow j\)的最短路径。显然不存在\(dist^\prime_{ij} < dist_{ij}\),否则我们可以直接走\(dist^\prime\)所对应的路径,可以构成一个更小的环。 换一句话说,设\(\mathrm{node}\)加入环的时间为\(t_i\),则两个可以直接连接的结点之间的加入环的时间差:\(\Delta t = 1\) 因此对于一个长度大于\(k\)的最小环,我们每隔一点进行输出,必定是一个大小大于\(\lceil \frac{k}{2} \rceil\)的独立集。
如上图所示,假如出现这种情况,直接取出\(\{2,3,4\}\)即可。
而我们如何确认这种割裂情况呢?我们先用\(\mathrm{dfs}\)任意找到一个圆环,对于每个边\(\mathrm{edge_i}\),设\(u,v\)为边的端点。假如\(u,v\)都在环中,且他们之间的距离不为\(1\),则说明这个环不是最小环,可以进一步规约。
总的思路如下:
①: 首先利用\(\mathrm{dfs}\)进行遍历一边黑白染色,一边记录每个结点\(\mathrm{node}\)出现的时间\(t_i\),看是否存在环即\(t_i\)已经进行赋值
②: 若找到环,便将\(t_i \rightarrow t_{cur}\)时间内所有加入的结点连接在一起,组成初始环(用deque存)
③: 若\(\mathrm{dfs}\)之后没有找到环,利用黑白染色输出结点,结束。
④: 若找到环,对先确认是否存在割裂情况,没有则转⑤,假如存在则将多余的结点从双端队列中弹出。
⑤: 判断大小,若小于\(k\)则直接输出,若大于\(k\)则间隔输出,结束。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 50;
vector<int> p, E[MAXN], col[2];
deque<int> cicrle;
int u[MAXN], v[MAXN], pos[MAXN];
bool in_cicrle[MAXN];
void dfs(int x, int fa= 0){
pos[x] = p.size();
p.push_back(x);
col[p.size() % 2].push_back(x); // 黑白染色
for (auto &go: E[x]){
if (go == fa) continue;
if (pos[go] == -1) dfs(go, x); // 是否访问过,考虑pos[i] == 0的情况
else {
if (cicrle.empty()){
for (int i = pos[go]; i <= pos[x]; ++ i){ // 将时间t_go -> t_x内假如的结点都加进去
cicrle.push_back(p[i]);
in_cicrle[p[i]] = true;
}
}
}
}
p.pop_back();
}
int main(){
int n, m, k;
cin >> n >> m >> k;
memset(pos, -1, sizeof(pos));
p.clear();
cicrle.clear();
for (int i = 0; i <= n; ++ i) E[i].clear();
for (int i = 0; i < m; ++ i){
cin >> u[i] >> v[i];
E[u[i]].push_back(v[i]);
E[v[i]].push_back(u[i]);
}
dfs(1);
if (cicrle.empty()){ // 情况③
cout << "1" << endl;
if (col[0].size() < col[1].size()) swap(col[0], col[1]);
for (int i = 0; i < ((k + 1) / 2); ++ i) cout << col[0][i] << " ";
cout << endl;
return 0;
}
for (int i = 0; i < m; ++ i){ // 情况④, 进行规约
if (in_cicrle[v[i]] && in_cicrle[u[i]] && abs(pos[v[i]] - pos[u[i]]) != 1){
while (cicrle.front() != v[i] && cicrle.front() != u[i]){
in_cicrle[cicrle.front()] = false;
cicrle.pop_front();
}
while (cicrle.back() != v[i] && cicrle.back() != u[i]){
in_cicrle[cicrle.back()] = false;
cicrle.pop_back();
}
}
}
if (cicrle.size() <= k){ // 情况⑤
cout << "2" << endl;
cout << cicrle.size() << endl;
for (int i = 0; i < cicrle.size(); ++ i) cout << cicrle[i] << " "; cout << endl;
}else {
cout << "1" << endl;
for (int i = 0; i < ((k + 1) / 2); ++ i){
cout << cicrle[2 * i] << " ";
}
cout << endl;
}
}
其实这题,最开始想直接求最小环,然后学习了\(\mathcal{Floyd - Warshall}\)算法求最小环,但是因为点太多了,时间复杂度肯定超过就没有用了。但是也碰到了一个坑:
mini_cicrle = min(mini_ciclre, edge[i][k] + edge[k][j] + dist[i][j])
,会出现三个INF
相加,所以假如INF = 0x3f3f3f3f
的话会直接溢出,需要注意!!!
\(\mathcal{E}\)题: \(\mathrm{X-OR}\)
bitmasks
interactive
*2700
这题是交互题,考察异或,\(\otimes\)的考点无非就是:
- \(x \otimes0 = x\)
- \(x \otimes x = 0\)
有一个固定的长度为 \(n\) 的排列 \(P\),其值域为 \([0,n-1]\),你可以进行不超过 \(4269\) 次询问,之后你需要输出这个排列 \(P\)。
利用性质一就好了,所以现在我们需要做到就是找到\(0\)的位置。
- 因为\(y \otimes x \leq x\),所以不存在其他的数异或x比0异或x要小,所以假如存在\(a \otimes x < b \otimes x\),则\(b\)不可能为\(0\) -- 结论\(1\)
- 因为\(x \otimes0 = x\),则如果\(a \not = b\),则必然有\(a \otimes 0 \not = b \otimes 0\),也就是说,如果存在一个数\(c\),使得\(a \not = b, a\otimes c = b\otimes c\),则\(c\)不可能为\(0\) -- 结论\(2\)
所以,我们可以先打乱顺序后任取两个\(fi\),\(se\)。然后顺去取后面的\(th\), \(val = \mathrm{Query(fi,se)} \quad temp = \mathrm{Query(fi,th)}\):
- 对于\(val > temp\)根据结论\(1\),\(se\)所在位置不可能为\(0\),所以\(th \rightarrow se, temp \rightarrow val\)
- 对于\(val < temp\)根据结论\(1\),不用更新
- 对于\(val == temp\)根据结论\(2\),\(fi\)所在位置不可能为\(0\),所以\(th \rightarrow fi, \mathrm{Query(se,th)} \rightarrow val\)
#include<bits/stdc++.h>
using namespace std;
const int MAXN = (1 << 11) + 50;
int n, p[MAXN];
vector<int> ans;
inline int query(int x, int y){
cout << "? " << x << ' ' << y << endl;
cout.flush();
int ret; cin >> ret;
return ret;
}
int main(){
ans.clear();
srand(20010410);
cin >> n;
for (int i = 0; i < n; ++ i) p[i] = i + 1;
random_shuffle(p, p + n);
int fi = p[0], se = p[1], val = query(fi, se);
for (int i = 2; i < n; ++ i){
int temp = query(se, p[i]);
if (temp > val) continue;
else if (temp < val){ // fi xor se > se xor th --> fi != zero
fi = p[i];
val = temp;
}else {
se = p[i];
val = query(fi, p[i]);
}
}
int zero_idx(0);
while (true){
int i = rand() % n + 1;
if (fi == i || se == i) continue;
int v1 = query(fi, i), v2 = query(se, i);
if (v1 == v2) continue;
zero_idx = v1 < v2 ? fi : se;
break;
}
for (int i = 1; i <= n; ++ i){
if (i != zero_idx) ans.push_back(query(i,zero_idx));
else ans.push_back(0);
}
cout << "! ";
for (auto &x: ans) cout << x << " ";
cout << endl;
cout.flush();
return 0;
}
\(\mathrm{Think\; twice,\; Code\; once}\)
Codeforces Round #649 (Div. 2)的更多相关文章
- Codeforces Round #649 (Div. 2) C. Ehab and Prefix MEXs
题目链接:https://codeforces.com/contest/1364/problem/C 题意 给出大小为 $n$ 的非递减数组 $a$,构造同样大小的数组 $b$,使得对于每个 $i$, ...
- Codeforces Round #649 (Div. 2) B. Most socially-distanced subsequence
题目链接:https://codeforces.com/contest/1364/problem/B 题意 给出大小为 $n$ 的一个排列 $p$,找出子序列 $s$,使得 $|s_1-s_2|+|s ...
- Codeforces Round #649 (Div. 2) A. XXXXX
题目链接:https://codeforces.com/contest/1364/problem/A 题意 找出大小为 $n$ 的数组 $a$ 的最长连续子数组,其元素和不被 $x$ 整除. 题解 如 ...
- Codeforces Round #649 (Div. 2) E. X-OR 交互 二进制 随机 期望
LINK:X-OR 本来是应该昨天晚上发的 可是昨天晚上 做这道题 写了一个分治做法 一直wa 然后查错 查不出来 心态崩了 想写对拍 发现交互库自己写不出来. 一系列sb操作 == 我都醉了. 今天 ...
- Codeforces Round #649 (Div. 2) C、Ehab and Prefix MEXs D、Ehab's Last Corollary 找环和点染色
题目链接:C.Ehab and Prefix MEXs 题意; 有长度为n的数组a(下标从1开始),要求构造一个相同长度的数组b,使得b1,b2,....bi集合中没有出现过的最小的数是ai. mex ...
- Codeforces Round #649 (Div. 2) C. Ehab and Prefix MEXs (构造,贪心)
题意:有长度为\(n\)的数组\(a\),要求构造一个相同长度的数组\(b\),使得\({b_{1},b_{2},....b_{i}}\)集合中没有出现过的最小的数是\(a_{i}\). 题解:完全可 ...
- Codeforces Round #649 (Div. 2) B. Most socially-distanced subsequence (数学,差分)
题意:有一长度为\(n\)的数组,求一子序列,要求子序列中两两差的绝对值最大,并且子序列尽可能短. 题解:将数组看成坐标轴上的点,其实就是求每个单调区间的端点,用差分数组来判断单调性. 代码: #in ...
- Codeforces Round #649 (Div. 2) A. XXXXX (贪心)
题意:有一个长度为\(n\)的数组,找一段最长子数组,使得其元素和为\(x\),如果存在,输出子数组的长度,否则输出\(-1\). 题解:这题我们要从元素和\(sum\)来考虑,首先,如果原数组的所有 ...
- Codeforces Round #366 (Div. 2) ABC
Codeforces Round #366 (Div. 2) A I hate that I love that I hate it水题 #I hate that I love that I hate ...
随机推荐
- requests顺序执行实现
多步请求封装,执行完一个用例 def requests(self,step_info): request_type =step_info['请求方式'] if request_type==" ...
- linux之平均负载(学习笔记非原创)
什么是平均负载 [root@111 ~]# uptime 11:03:33 up 149 days, 17:34, 1 user, load average: 0.08, 0.05, 0.01 最后三 ...
- 基础篇-http协议《http 简介、url详解、request》
目录 一.http 简介 二.url 详解 三.request 1.get 和 post 2.请求方法 3.request 组成 4.请求头 5.get 请求参数 6.post 请求参数 7.post ...
- python zxing包解析二维码报UnicodeDecodeError错误解决办法
一般错误的原因是这个库不支持中文的解码(二维码内容包含中文). 修改如下: 进入zxing.__init__.py代码中,类BarCode下,parse方法中: 找到下面这两行原代码如下: 1 raw ...
- 代码页(CodePage)列表
代码页编号 国家地区或语言37 IBM037 IBM EBCDIC (US-Canada) 437 IBM437 ...
- Ubuntu20.04安装Typora
Ubuntu20.04安装Typora 安装方法 # optional, but recommended sudo apt-key adv --keyserver keyserver.ubuntu.c ...
- RPC 实战与原理 精简版
什么是 RPC? RPC 有什么作用? RPC 步骤 为什么需要序列化? 零拷贝 什么是零拷贝? 为什么需要零拷贝? 如何实现零拷贝? Netty 的零拷贝有何不同? 动态代理实现 HTTP/2 特性 ...
- Less中Css预处理器
Less.js 安装 npm install -g less 变量 basic 变量采用@进行变量定义.变量可以直接参加运算. @width:100px; .variables{ width:@wid ...
- 支付宝沙箱环境使用(Alipay Easy SDK ) .Net示例
新版服务端 SDK(Alipay Easy SDK)适用于 Java.C#.PHP 编程语言,对开放能力的 API 进行了更加贴近高频场景的精心设计与裁剪,简化了服务端调用方式,让开发者享受极简编程体 ...
- .net core 不同地区时间相互转换
.net core 不同地区时间相互转换 //韩国时间转换成当前时间 //value=需要转换的时间 //Korea Standard Tim 韩国时间 //China Standard Time 中 ...