CodeForces1602

Two Subsequences

解析:

题目大意

给你一个字符串 \(s\)。你需要两个非空字符串 \(a\) 和 \(b\) 并且满足下面的条件:

  1. 字符串 \(a\) 和 \(b\) 都是 \(s\) 的子序列。
  2. 对于原字符串的每一个字符,必须属于 \(a\) 和 \(b\) 之一。
  3. \(a\) 是所有满足条件的字符串中字典序最小的。

给你 \(s\),输出 \(a\) 和 \(b\)。


思路:

字典序最小,首先需要长度最小,所以 \(a\) 字符串最多只要一个字符就行,那么只在字符串中选择字典序最小的字符当做 \(a\) 即可,剩余作为 \(b\) 即可。


code

#include <bits/stdc++.h>
using namespace std;
const int N = 100 + 10;
const int INF = 0x3f3f3f3f;
inline int read ()
{
int x = 0, f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
return x * f;
}
int n;
char s[N];
void solve ()
{
scanf ("%s", s + 1);
n = strlen (s + 1);
int mn = INF;
for (int i = 1; i <= n; i++) mn = min (mn, int(s[i] - 'a'));
printf ("%c ", char (mn + 'a'));
bool flag = false;
for (int i = 1; i <= n; i++) { if (!flag && int(s[i] - 'a') == mn) { flag = true; continue; } printf ("%c", s[i]); }
printf ("\n");
}
signed main()
{
int t = read ();
while (t--) solve ();
return 0;
}

Divine Array

解析:

题目大意:

给定一个序列,一次转换是将一个数变成这个数在这个序列中出现的次数。

序列 \(\{2,1,1,4,3,1,2\}\) 中,\(2\) 出现 \(2\) 次,\(1\) 出现 \(3\) 次,\(3\) 和 \(4\) 出现 \(1\) 次,那么这个序列进行一次转换之后就变成了 \(\{2,3,3,1,1,3,2\}\),同理,进行两次转换后是 \(\{2,3,3,2,2,3,2\}\),进行三次转换后是 \(\{4,3,3,4,4,3,4\}\)。

有 \(q\) 次询问,每次询问第 \(x\) 个位置的元素经过 \(k\) 次转换之后是什么。


思路:

先给出个显然的结论:这个序列在变化 \(\log n\) 次后就会稳定。

考虑每次变化,实际上是两个出现次数相同的数合并的过程,那么每次合并完成新数的出现次数就会变成原数的两倍,即最多 \(\log\) 次就会令整个序列变成一个数。

证毕。

考虑暴力 \(\log\) 次变化后的序列,离线处理答案即可。

时间复杂度 \(\mathcal O(n\log n+q)\)。


code:

#include <bits/stdc++.h>
using namespace std;
const int N = 2000 + 10;
const int M = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const int mods = 998244353;
typedef pair <int, int> pii;
inline int read ()
{
int x = 0, f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
return x * f;
}
int n, m;
int a[N];
struct Query {
int x, k, id;
bool operator < (const Query &A) const {
return k < A.k;
}
}q[M];
int buttle[N], ans[M];
void solve ()
{
n = read ();
for (int i = 1; i <= n; i++) a[i] = read ();
m = read ();
for (int i = 1; i <= m; i++) q[i].x = read (), q[i].k = read (), q[i].id = i;
sort (q + 1, q + m + 1);
int now = 1;
for (int cnt = 0; ; cnt++)
{
while (now <= m && q[now].k <= cnt) ans[q[now].id] = a[q[now].x], now++;
for (int i = 1; i <= n; i++) buttle[i] = 0;
for (int i = 1; i <= n; i++) buttle[a[i]]++;
bool flag = true;
for (int i = 1; i <= n; i++) if (buttle[i] != i && buttle[i]) flag = false;
if (flag) break;
for (int i = 1; i <= n; i++) a[i] = buttle[a[i]];
}
while (now <= m) ans[q[now].id] = a[q[now].x], now++;
for (int i = 1; i <= m; i++) printf ("%lld\n", ans[i]);
}
signed main()
{
int t = read ();
while (t--) solve ();
return 0;
}

Array Elimination

解析:

题目大意

给你一个由非负整数组成的长度为 \(n\) 的序列 \(a_1,a_2,\cdots,a_n\),你可以先选择一个整数 \(k\),并在原序列中选择 \(k\) 个位置 \(i_1,i_2,\cdots i_k\),设 \(x=a_{i_1}\) & \(a_{i_2}\) & \(\cdots\) & \(a_{i_k}\),令它们全部减去 \(x\),求可以通过若干次操作使得序列中每个元素都等于 \(0\) 的所有可行的 \(k\)。


思路:

考虑拆位,对于每一个二进制位分开考虑,很明显,如果当前二进制位有 \(cnt\) 个 \(1\),那么选择的 \(k\) 必定需要是 \(cnt\) 的因子,因为如果剩余 \(1\) 的个数小于 \(k\) 那么这 \(cnt\) 个 \(1\) 就不可能被消掉了。

原问题转化为 \(\log\) 位的 \(cnt\) 的公约数,可以先求出来所有 \(cnt\) 的 \(\gcd\),答案为 \(\gcd\) 的所有因数。


code

#include <bits/stdc++.h>
#define eb emplace_back
using namespace std;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
typedef pair <int, int> pii;
inline int read ()
{
int x = 0, f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
return x * f;
}
int n;
int a[N];
vector <int> vec, ans;
int gcd (int a, int b) { return !b ? a : gcd (b, a % b); }
void solve ()
{
vec.clear (), ans.clear ();
n = read ();
int Or = 0;
for (int i = 1; i <= n; i++) a[i] = read (), Or |= a[i];
if (!Or)
{
for (int i = 1; i <= n; i++) printf ("%lld ", i);
puts ("");
return ;
}
int k;
for (int i = 30; i >= 0; i--) if (Or & (1 << i)) { k = i; break; }
for (; k >= 0; k--)
{
int cnt = 0;
for (int i = 1; i <= n; i++) cnt += ((a[i] >> k) & 1);
if (cnt) vec.eb (cnt);
}
int g = vec[0];
for (int i = 1; i < vec.size (); i++) g = gcd (g, vec[i]);
for (int i = 1; i * i <= g; i++)
if (g % i == 0)
{
ans.eb (i);
if (i * i != g) ans.eb (g / i);
}
sort (ans.begin (), ans.end ());
for (int i = 0; i < ans.size (); i++) printf ("%d ", ans[i]);
puts ("");
}
signed main()
{
int t = read ();
while (t--) solve ();
return 0;
}

Frog Traveler

解析:

题目大意:

你是一直青蛙,最一开始在井底深 \(d\) 处,当你处于深度 \(i\) 时,你每次可以跳 \(h\in[0,a_i]\) 米,但当你跳到深度 \(j\) 米后,你将会下滑 \(b_j\) 米,请问最少要跳多少次才能到达井外(0 处)。


思路:

平衡树优化bfs,我们考虑暴力的过程,每次从当前点往前枚举 \([i-a_i,i-1]\),更新跳到这些位置后下滑到的深度所需要的跳跃次数,并把这些的新的位置加入队列。

我们发现,这样如果枚举的复杂度上限是 \(\mathcal O(n^2)\)(虽然达不到这标准并且这题可以卡常过去,但我们应追求正解),无法通过,考虑每次枚举跳到的位置实际上是一个区间,这就启发了可以用平衡树维护当前还没有被跳到的过的深度,那么在每次枚举完成后将这段区间直接在平衡树上删除,这样每个点最多会被删除一次。

优化完的代码时间复杂度 \(\mathcal O(n\log n)\)(细节较多,非常阴间的题)。


code:

#include <bits/stdc++.h>
#define eb emplace_back
using namespace std;
const int N = 3e5 + 10;
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair <int, int> pii;
inline int read ()
{
int x = 0, f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
return x * f;
}
int n, root;
int a[N], b[N];
int dp[N];
queue <int> que;
int lst[N], lst2[N];
vector <int> ans;
struct fhq_treap {
int node;
int x, y, z;
struct Tree {
int ls, rs;
int key, val, siz, pos;
#define ls(x) tree[x].ls
#define rs(x) tree[x].rs
#define key(x) tree[x].key
#define val(x) tree[x].val
#define siz(x) tree[x].siz
#define pos(x) tree[x].pos
} tree[N];
inline void pushup (int x) { siz(x) = siz(ls(x)) + siz(rs(x)) + 1; }
int new_node (int v, int x)
{
siz(++node) = 1;
key(node) = rand ();
val(node) = v;
pos(node) = x;
return node;
}
int merge (int x, int y)
{
if (!x || !y) return x + y;
if (key(x) < key(y)) { rs(x) = merge (rs(x), y); pushup (x); return x; }
else { ls(y) = merge (x, ls(y)); pushup (y); return y; }
}
void split (int now, int k, int &x, int &y)
{
if (!now) {x = y = 0; return ; }
if (val(now) <= k) x = now, split (rs(now), k, rs(x), y);
else y = now, split (ls(now), k, x, ls(y));
pushup (now);
}
void insert (int v, int p)
{
x = y = 0;
split (root, v, x, y);
root = merge (merge (x, new_node (v, p)), y);
}
void dfs (int now, int x)
{
if (!now) return ;
if (dp[pos(now)] > dp[x] + 1)
que.push (pos(now)), lst[pos(now)] = x, lst2[pos(now)] = val(now);
dp[pos(now)] = min (dp[pos(now)], dp[x] + 1);
if (!val(now)) { lst[pos(now)] = x; return ; }
dfs (ls(now), x);
dfs (rs(now), x);
}
void solve (int p)
{
int l = p - a[p] - 1, r = p - bool(a[p]);
split (root, l, x, y);
split (y, r, y, z);
dfs (y, p);
root = merge (x, z);
}
} T;
signed main()
{
n = read ();
for (int i = 1; i <= n; i++) a[i] = read ();
for (int i = 1; i <= n; i++) b[i] = read (), T.insert (i, i + b[i]);
memset (dp, 0x3f, sizeof (dp));
dp[n] = 0;
que.push (n); T.insert (0, 0);
while (!que.empty ())
{
int x = que.front (); que.pop ();
T.solve (x);
if (dp[0] != INF) break;
}
if (dp[0] == INF) printf ("-1\n");
else
{
printf ("%d\n", dp[0]);
int now = 0;
while (now != n)
{
ans.eb (lst2[now]);
now = lst[now];
}
for (int i = ans.size () - 1; i >= 0; i--) printf ("%d ", ans[i]);
}
return 0;
}

Optimal Insertion

解析:

题目大意:

给你两个序列 \(a,b\),长度分别为 \(n,m\)。现在要把 \(b\) 序列中的每个数插入 \(a\),得到一个长度为 \(n+m\) 的序列 \(c\),求逆序对个数最少的插入方法。逆序对的定义为点对 \((i,j)\) 满足 \(i<j\),\(a_i>a_j\)。

注意,\(b\) 序列可以随意插入,但 \(a\) 序列对应顺序不应发生改变。


思路:

我们定义 \(b_i\) 的最优决策位置的意义是满足在只把 \(b_i\) 插入原序列 \(a\) 中后,形成的逆序对个数最少的位置。

那么有一个结论:大数的最优决策点一定在较小数的最优决策点后面,那么最优解所对应的 \(b\) 在 \(a\) 中的顺序也应为升序排列,这样就可以对 \(b\) 按升序排序后依次插入 \(a\)。

考虑证明一下这个结论:设 \(b_i\) 的最优决策点为 \(p_i\),\(b_j\) 的最优决策点为 \(p_j\),满足 \(b_i>b_j\) 且 \(p_i<p_j\),那么考虑 \(p_i\) 之前以及 \(p_j\) 之后的数都不会对逆序对产生贡献,所以考虑 \([p_i,p_j]\) 之间的数,我们考虑分为三类:\(x> b_i\) 的个数记为 \(cnt_1\),\(b_j\leq x\leq b_i\) 的个数记为 \(cnt_2\),以及 \(x<p_j\) 的记为 \(cnt_3\),那么根据最优决策点的定义,有:\(cnt_1+cnt_2<cnt_3\),即 \(b_j\) 放在 \(p_j\) 的位置产生的逆序对小于放在 \(p_i\) 位置逆序对的个数。那么同样有 \(cnt_2+cnt_3<cnt_1\)(考虑 \(b_i\) 放在 \(p_i,p_j\)),那么与 \(cnt_1+cnt_2\) 矛盾,\(b_i>b_j\) 且 \(p_i<p_j\) 的假设不成立,则有 \(p_i>p_j\)。

这个结论并不能提供具体算法,但可以告诉我们插入的时候的顺序,并且 \(b\) 序列之间不会产生逆序对,也就是说我们只需要计算 \(a\) 内部的逆序对以及 \(a\) 与 \(b\) 产生的逆序对。

前者的计算是平凡的,可以直接 \(\mathcal O(n\log n)\) 树状数组计算,我们考虑如何计算后者。

我们维护一个长度为 \(n+1\) 的线段树,维护放当前 \(b_i\) 时在序列 \(a\) 对应位置产生的贡献,那么对于 \(a_j>b_i\) 的贡献将贡献在 \([j+1,n+1]\),对于 \(a_j<b_i\) 的贡献区间应为 \([1,j]\)。

由于 \(b\) 是单调不降的,所以我们可以动态维护 \(a_j\) 产生贡献的方向,具体看代码。

原问题变为区间 \(\pm 1\),全局最小值。

总时间复杂度 \(\mathcal O((n+m)\log n+n\log n)\)。


code:

#include <bits/stdc++.h>
#define eb emplace_back
#define lob lower_bound
using namespace std;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const int mods = 1e9 + 7;
typedef pair <int, int> pii;
inline int read ()
{
int x = 0, f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
return x * f;
}
int n, m;
int a[N], b[N];
int ls[N << 1], len;
vector <int> vec[N << 1];
int buttle[N << 1];
struct Seg_tree {
#define lson rt << 1
#define rson rt << 1 | 1
struct seg_tree {
int mn, tag;
#define mn(rt) tree[rt].mn
#define tag(rt) tree[rt].tag
} tree[N << 2];
inline void pushup (int rt) { mn(rt) = min(mn(lson), mn(rson)); }
inline void spread (int rt)
{
if (tag(rt) != 0)
{
mn(lson) += tag(rt); mn(rson) += tag(rt);
tag(lson) += tag(rt); tag(rson) += tag(rt);
tag(rt) = 0;
}
}
void modify (int rt, int l, int r, int L, int R, int k)
{
if (L <= l && r <= R) { mn(rt) += k; tag(rt) += k; return ; }
int mid = (l + r) >> 1;
spread (rt);
if (L <= mid) modify (lson, l, mid, L, R, k);
if (R > mid) modify (rson, mid + 1, r, L, R, k);
pushup (rt);
}
} T;
struct BIT {
#define lowbit(x) x & (-x)
int tree[N << 1];
void add (int x, int val) { for (; x <= len; x += lowbit(x)) tree[x] += val; }
int query (int x) { int res = 0; for (; x > 0; x -= lowbit(x)) res += tree[x]; return res; }
} T2;
void solve ()
{
len = 0;
n = read (), m = read ();
for (int i = 1; i <= n; i++) a[i] = read (), ls[++len] = a[i];
for (int i = 1; i <= m; i++) b[i] = read (), ls[++len] = b[i];
sort (ls + 1, ls + len + 1);
sort (b + 1, b + m + 1);
len = unique (ls + 1, ls + len + 1) - ls - 1;
long long ans = 0;
for (int i = n; i >= 1; i--)
{
a[i] = lob (ls + 1, ls + len + 1, a[i]) - ls;
vec[a[i]].eb (i);
T.modify (1, 1, n + 1, i + 1, n + 1, 1);
ans += (long long)T2.query(a[i] - 1); T2.add (a[i], 1);
}
for (int i = 1; i <= m; i++) b[i] = lob (ls + 1, ls + len + 1, b[i]) - ls, buttle[b[i]]++;
int now = 1;
for (int i = 1; i <= m; i += buttle[b[i]])
{
while (now < b[i] && !vec[now].empty ())
{
for (int j = 0; j < vec[now].size (); j++)
T.modify (1, 1, n + 1, vec[now][j] + 1, n + 1, -1),
T.modify (1, 1, n + 1, 1, vec[now][j], 1);
now++;
}
if (now == b[i])
for (int j = 0; j < vec[now].size (); j++)
T.modify (1, 1, n + 1, vec[now][j] + 1, n + 1, -1);
ans += 1ll * T.tree[1].mn * buttle[b[i]];
if (now == b[i])
for (int j = 0; j < vec[now].size (); j++)
T.modify (1, 1, n + 1, 1, vec[now][j], 1);
now++;
}
for (int i = 1; i <= n + m; i++) T2.tree[i] = 0, buttle[i] = 0, vec[i].clear ();
for (int i = 1; i <= (n << 2); i++) T.tree[i].mn = T.tree[i].tag = 0;
printf ("%lld\n", ans);
}
signed main()
{
int t = read ();
while (t--) solve ();
return 0;
}

6

解析:

题目大意:

咕咕咕


思路:


code:


Codeforces Round #751 (Div. 2)/CodeForces1602的更多相关文章

  1. 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 ...

  2. Codeforces Round #354 (Div. 2) ABCD

    Codeforces Round #354 (Div. 2) Problems     # Name     A Nicholas and Permutation standard input/out ...

  3. Codeforces Round #368 (Div. 2)

    直达–>Codeforces Round #368 (Div. 2) A Brain’s Photos 给你一个NxM的矩阵,一个字母代表一种颜色,如果有”C”,”M”,”Y”三种中任意一种就输 ...

  4. cf之路,1,Codeforces Round #345 (Div. 2)

     cf之路,1,Codeforces Round #345 (Div. 2) ps:昨天第一次参加cf比赛,比赛之前为了熟悉下cf比赛题目的难度.所以做了round#345连试试水的深浅.....   ...

  5. Codeforces Round #279 (Div. 2) ABCDE

    Codeforces Round #279 (Div. 2) 做得我都变绿了! Problems     # Name     A Team Olympiad standard input/outpu ...

  6. Codeforces Round #262 (Div. 2) 1003

    Codeforces Round #262 (Div. 2) 1003 C. Present time limit per test 2 seconds memory limit per test 2 ...

  7. Codeforces Round #262 (Div. 2) 1004

    Codeforces Round #262 (Div. 2) 1004 D. Little Victor and Set time limit per test 1 second memory lim ...

  8. Codeforces Round #371 (Div. 1)

    A: 题目大意: 在一个multiset中要求支持3种操作: 1.增加一个数 2.删去一个数 3.给出一个01序列,问multiset中有多少这样的数,把它的十进制表示中的奇数改成1,偶数改成0后和给 ...

  9. Codeforces Round #268 (Div. 2) ABCD

    CF469 Codeforces Round #268 (Div. 2) http://codeforces.com/contest/469 开学了,时间少,水题就不写题解了,不水的题也不写这么详细了 ...

随机推荐

  1. 使用java随机生成有个性的用户名,LOL地名+水浒传,合计2808个有意思的用户名

    * 随机生成用户名 * 取水浒传108好汉名字 * 取LOL地名26个,组合而成 * 一共可以生成2808个不同特色的用户名 如果你在上网的时候,用户名难取的话,这里有很多可选择的用户名,现提供100 ...

  2. Apache DolphinScheduler 1.2.0 task 任务存储结构说明

    本文章经授权转载 Table of Contents 任务总体存储 Shell节点 SQL节点 存储过程节点 SPARK节点 MapReduce(MR)节点 Python节点 Flink节点 HTTP ...

  3. 活动回顾|Apache DolphinScheduler x Pulsar 在线 Meetup

    关于 Apache DolphinScheduler: " Apache DolphinScheduler(Incubating) 是一个分布式去中心化.易扩展的可视化工作流任务调度系统,致 ...

  4. python包合集-cffi

    一.cffi cffi是连接Python与c的桥梁,可实现在Python中调用c文件.cffi为c语言的外部接口,在Python中使用该接口可以实现在Python中使用外部c文件的数据结构及函数. 二 ...

  5. 部署 Vite 静态网站到 Gitee Pages

    开启仓库的 Gitee Pages 服务,部署目录 dist/: Vite 构建 build,输出到 dist 文件:取消 .gitignore 中的 dist: Vite preview 查看静态网 ...

  6. 在 Linux 系统中安装 Node.js 的流程

    下载资源包 在 NodeJS 官网下载压缩包: 将压缩包中的 node-v14.17.0-linux-x64.tar 拖出来,只需要里面的 tar 压缩包. 解压到 Linux 目录中 解压压缩包到当 ...

  7. Linux虚拟机报错grub rescue解决步骤

    /boot 分区内核文件丢失 实验准备 1) 准备:rm -rf /boot/* 2) 系统启动报错截图 修复步骤 重启显示logo时 按 Esc,选择从光驱启动 或者关机再选择打开电源时进入固件 移 ...

  8. 富莱尔ERP

    微型版本 按年付费模式 980包含5个用户,后续每增加一个用户加298元. 适合创业型企业,想尝试用下ERP软件解决打送货单问题,客户自动生成账单,自动统计业务员每月业绩功能. 试用账号:wxadmi ...

  9. 微软出品自动化神器Playwright,不用写一行代码(Playwright+Java)系列(一) 之 环境搭建及脚本录制

    一.前言 半年前,偶然在视频号刷到某机构正在直播讲解Playwright框架的使用,就看了一会,感觉还不错,便被种草,就想着自己有时间也可以自己学一下,这一想着就半年多过去了. 读到这,你可能就去百度 ...

  10. python的环境,你再也不用愁-conda

    Conda Guide Conda简介 conda是一个包,依赖和环境管理工具,适用于多种语言,如: Python, R, Scala, Java, Javascript, C/ C++, FORTR ...