标签:位置 c++ 题解 while 顺序 memset 异或 force tps
Greedy
implementation
*1200
第一题,要求的是求一段子数组的区间和,要求该区间和不被\(x\)整除且长度尽可能长。
显然,对于这类题目可以想到以下几点:
思路如下:定义数组为\(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;
}
Greedy
two pointers
*1300
\(\mathcal{B}\) 题的话从题干中获取如下两点就好了:
根据分析,当序列\(\{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;
}
}
Greedy
constructive algorithms
*1600
需要注意的是
It‘s guaranteed that \(a_i\leq a_{i+1}\) for \(1\leq i\leq n\).
一共有更具MEX的定义可以获得两点信息:
首先是比赛中的方法,通过维护一个链表(链表中是还没有被添加的数字),每次要加入时判断还有不要小于\(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;
}
dfs
graphs
trees
*2100
extra: 最小环
这题,看了半天才会写。这题要求的是对于给出一张 \(n\) 个点的无向连通图和一个常数 \(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:假如存在环,也有两种情况:
如上图所示,假如出现这种情况,直接取出\(\{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
的话会直接溢出,需要注意!!!
bitmasks
interactive
*2700
这题是交互题,考察异或,\(\otimes\)的考点无非就是:
有一个固定的长度为 \(n\) 的排列 \(P\),其值域为 \([0,n-1]\),你可以进行不超过 \(4269\) 次询问,之后你需要输出这个排列 \(P\)。
利用性质一就好了,所以现在我们需要做到就是找到\(0\)的位置。
所以,我们可以先打乱顺序后任取两个\(fi\),\(se\)。然后顺去取后面的\(th\), \(val = \mathrm{Query(fi,se)} \quad temp = \mathrm{Query(fi,th)}\):
#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)
标签:位置 c++ 题解 while 顺序 memset 异或 force tps
原文地址:https://www.cnblogs.com/Last--Whisper/p/13143897.html