Hello 2023
写在前面
比赛地址:https://codeforces.com/contest/1779。
我受不了了飞鸟凑完全是我的理想型我草为什么不能有我和凑的 HS 调色板社你非要给这么好的孩子带个把是什么意思懂不懂 16 年 gal 角色人气一位的含金量啊玩少女领域哪有不喜欢飞鸟凑的硬撑罢了我真的不是南通但是我真的好喜欢飞鸟凑啊为了你我要在打完 CF 之后还要熬夜熬到三点推少女领域妈的忍不了了一拳把地球打爆🥵🥵🥵
差不多得了.jpg
A
特判。
LL...LLRRR...RRR
型则交换分界处的 LR
,LLL...LLL
和 RRR...RRR
无解,其余情况均合法。
赛时写的比较抽象。
//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
//=============================================================
char s[100010];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
//=============================================================
int main() {
int T = read();
while (T --) {
int n = read(), l = -1000 * n, r = 1000 * n;
scanf("%s", s + 1);
for (int i = 1; i <= n; ++ i) {
if (s[i] == 'R') {
r = i + 1;
break;
}
}
for (int i = n; i >= 1; -- i) {
if (s[i] == 'L') {
l = i - 1;
break;
}
}
if (l >= r - 1) {
printf("0\n");
continue;
}
if (l == r - 3) {
printf("%d\n", l + 1);
continue;
}
printf("-1\n");
}
return 0;
}
B
\(n\) 为偶数时,令 \(s_i = (-1)^i\) 即可。
\(n\) 为奇数时,记 \(t = \sum s_i\),手玩几组数据发现有:
- \(n=3\) 时,无解。
- \(n=5\) 时,有:\(s = t, -2t, t, -2t, t\)。
- \(n=7\) 时,有:\(s = 2t, -3t, 2t, -3t, 2t, -3t, 2t\)。
- \(n\) 更大的情况以此类推。
赛时试了一下取 \(t=1\) 正好合法,证明详见官方题解。
//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
//=============================================================
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
//=============================================================
int main() {
int T = read();
while (T --) {
int n = read();
if (n % 2 == 0) {
printf("YES\n");
for (int i = 1; i <= n; ++ i) {
if (i % 2 == 0) printf("-");
printf("1 ");
}
} else {
if (n == 3) {
printf("NO\n");
continue;
}
printf("YES\n");
for (int i = 1; i <= n; ++ i) {
if (i % 2 == 1) printf("-%d ", n / 2 - 1);
else printf("%d ", n / 2);
}
}
printf("\n");
}
return 0;
}
/*
s1 s2 s3 s4 s5
s3 + s4 + s5 = 0
s1 + s4 + s5 = 0
s3 = s1
s1 + s2 + s5 = 0
s2 = s4
s1 + s2 + s3 = 0
s3 = s5
s1 = s3 = s5
s2 = s4
s5 + s4 + s5 = 0
s5 = -sum
s4 + s5 = sum
s4 = 2sum
-sum 2sum -sum 2sum -sum = sum
-1 2 -1 2 -1
-2sum 3sum -2sum 3sum -2sum 3sum -2sum = sum
-2 3 -2 3 -2 3 -2
-3 4 -3 4 -3 4 -3 4 -3
*/
C
\(t\) 组数据,每组数据给定参数 \(n,m\) 和一长度为 \(n\) 的整数列 \(a\)。
给定一种操作,每次操作可选择数列中的一个数,将这个数变为它的相反数。求至少进行多少次操作,可使数列 \(a\) 满足:对于所有 \(1\le k\le n\),有 \(\sum\limits_{i=1}^{k} a_i \ge \sum\limits_{i=1}^{m} a_i\) 成立。
\(1\le t\le 10^4, 1\le m\le n\le 2\times 10^5, -10^9\le a_i\le 10^9, \sum n\le 2\times 10^5\)。
2S,256MB。
赛时写了个二分答案调了一个半小时没调出来最后把二分删了就过了哈哈
首先,显然需要保证 \(a_m<0\)。之后对条件移项成 \(\sum\limits_{i=1}^{k} a_i -\sum\limits_{i=1}^{m} a_i\ge 0\) ,再分别考虑 \(k<m\) 和 \(k>m\) 的情况。
对于 \(k>m\),容易发现对 \(1\sim m\) 的操作并不会影响 \(\sum\limits_{i=1}^{k} a_i -\sum\limits_{i=1}^{m} a_i= \sum\limits_{i=m+1}^{k} a_i\) 的值。问题变为最少通过多少次操作使得 \(\sum\limits_{i=m+1}^{k} a_i\ge 0\) 均成立。显然,我们仅会对 \(a_i<0\) 进行操作。考虑从 \(k+1\) 开始正序枚举 \(a_k\),计算上述和式的值并贪心地进行操作。若某次计算的和式不满足条件,则不断地从 \(m+1\sim k\) 中选择出最小的 \(a_i\),对其进行操作使得和式的值增大。枚举时顺便维护一个小根堆即可。
之后考虑 \(k<m\),问题变为最少通过多少次操作使得 \(\sum\limits_{i=k+1}^{m} a_i\le 0\) 均成立,发现问题的形式是类似的,同样考虑边枚举边贪心地操作。此时我们仅会对 \(a_i>0\) 进行操作。考虑从 \(m\) 倒序枚举 \(a_k\),计算上述和式的值,同时维护 \(k+1\sim m\) 中 \(a_i\) 的最大值,若某次计算的和式不满足条件,则对维护的最大的 \(a_i\) 进行操作,使 \(\sum\limits_{i=1}^{m} a_i\) 的值减小,而不影响 \(\sum\limits_{i=1}^{k} a_i\) 的值,从而使和式的值减小。
优先队列简单实现即可。总复杂度为 \(O(n\log n)\) 级别。
//By:Luckyblock
/*
*/
#include <queue>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
LL a[kN], sum[kN];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
int T = read();
while (T --) {
int n = read(), m = read(), ans = 0;
for (int i = 1; i <= n; ++ i) {
a[i] = read();
sum[i] = sum[i - 1] + a[i];
}
LL nowsumm = sum[m];
std::priority_queue <LL> q1, q2;
q1.push(a[m]);
for (int i = m - 1; i; -- i) {
while (sum[i] < nowsumm) {
LL maxa = q1.top(); q1.pop();
nowsumm -= 2ll * maxa;
++ ans;
}
q1.push(a[i]);
}
LL nowsum = nowsumm;
for (int i = m + 1; i <= n; ++ i) {
nowsum += a[i];
q2.push(-a[i]);
while (nowsum < nowsumm) {
LL maxa = q2.top(); q2.pop();
nowsum += 2ll * maxa;
++ ans;
}
}
printf("%d\n", ans);
}
return 0;
}
D
\(t\) 组数据,每组数据给定两个长度为 \(n\) 的数列 \(a,b\),一个长度为 \(m\) 的数列 \(x\)。可以对数列进行不多于 \(m\) 次操作,每次操作可以选定参数 \(x_i,l,r\),令数列 \(a\) 中 \(a_j=\min(a_j, x_i)(l\le j\le r)\)。数列 \(x\) 中的每个元素只能作为一次参数。
判断经过任意次操作后,能否将数列 \(a\) 转化为数列 \(b\)。
\(1\le t\le 2\times 10^4\),\(3\le n\le 2\times 10^5\),\(1\le a_i,b_i,x_i\le 10^9\),\(1\le m\le 2\times 10^5\),\(\sum n, \sum m\le 2\times 10^5\)。
2S,256MB。
模拟题?大概可以这么说。
首先可以发现,如果有:\(x_i\notin \{b_i\}\),则使用此 \(x_i\) 进行操作没有贡献。
之后考虑按照 \(x_i\) 降序进行操作,这样每个位置仅会受与 \(b_i\) 相等的 \(x_i\) 影响一次,在此之前进行的操作并不会造成影响。
之后贪心地进行操作,对于某个 \(x_i\),首先找到没有被修改过的 \(b_j=x_i\) 的位置,然后在此位置附近贪心地增大修改区间,使区间覆盖所有 \(b_k < b_j\) 的位置 \(k\)。每次操作后都检查这次操作中有多少位置被修改成了目标大小,如果操作过程中所有位置均被修改,则有解,否则无解。
复杂度 \(O(n+m\log m)\) 级别。
//By:Luckyblock
/*
*/
#include <map>
#include <queue>
#include <cstdio>
#include <cctype>
#include <algorithm>
const int kN = 2e5 + 10;
//=============================================================
int n, m, a[kN], b[kN], x[kN], pos[kN];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
namespace ST {
int Log2[kN], f[kN][19];
void MakeST (int *a_) {
Log2[1] = 0;
for (int i = 1; i <= n; ++ i) {
f[i][0] = a_[i];
if (i > 1) Log2[i] = Log2[i >> 1] + 1;
}
for (int i = 1; i <= 18; ++ i) {
for (int j = 1; j + (1 << i) - 1 <= n; ++ j) {
f[j][i] = std::max(f[j][i - 1],
f[j + (1 << (i - 1))][i - 1]);
}
}
}
int Query(int l_, int r_) {
int lth = Log2[r_ - l_ + 1];
return std::max(f[l_][lth], f[r_ - (1 << lth) + 1][lth]);
}
}
bool cmppos(int fir_, int sec_) {
if (b[fir_] != b[sec_]) return b[fir_] > b[sec_];
return fir_ < sec_;
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
int T = read();
while (T --) {
n = read();
for (int i = 1; i <= n; ++ i) a[i] = read();
for (int i = 1; i <= n; ++ i) b[i] = read(), pos[i] = i;
ST::MakeST(b);
m = read();
for (int i = 1; i <= m; ++ i) x[i] = read();
std::sort(x + 1, x + m + 1);
std::sort(pos + 1, pos + n + 1, cmppos);
bool flag = 1;
for (int i = 1; i <= n; ++ i) {
if (b[i] > a[i]) flag = 0;
}
if (!flag) {
printf("NO\n");
continue;
}
flag = 0;
for (int i = m, j = 1; i; -- i) {
while (j <= n && a[pos[j]] == b[pos[j]]) ++ j;
if (j == n + 1){
flag = 1;
break;
}
int p = pos[j], val = b[p], l = p, r = p;
if (x[i] < val) {
flag = 0;
break;
}
if (x[i] != val) continue;
for (int L = 1, R = p; L <= R; ) {
int mid = (L + R) >> 1;
if (L == R) {
if (b[L] <= val) l = L;
break;
}
if (ST::Query(mid + 1, p) <= val) {
l = mid + 1;
R = mid;
} else {
L = mid + 1;
}
}
for (int L = p, R = n; L <= R; ) {
int mid = (L + R) >> 1;
if (L == R) {
if (b[R] <= val) r = R;
break;
}
if (ST::Query(p, mid) <= val) {
r = mid;
L = mid + 1;
} else {
R = mid;
}
}
while (j <= n) {
if (a[pos[j]] == b[pos[j]]) {
++ j;
continue;
}
if (b[pos[j]] < val) break;
if (l <= pos[j] && pos[j] <= r) {
++ j;
} else {
break;
}
}
if (j == n + 1) {
flag = 1;
break;
}
}
printf("%s\n", flag ? "YES" : "NO");
}
return 0;
}
E
交互
没做过交互,改天找个简单的试试。
不用改天了,今天就试试。
F
给定一棵 \(n\) 个节点的有根树,根节点为 1,点有点权 \(a_i\)。可以进行一种操作,每次操作选定节点 \(i\),将以节点 \(i\) 为根的子树中所有节点的点权值更改为子树点权值异或和。要求构造一种操作方案,使得整棵树所有点的点权值均为 0,且操作次数不大于 \(2n\) 次。
\(2\le n\le 2\times 10^5\),\(0\le a_i\le 31\)。
4S,512MB。
不看数据范围是傻逼
还有输出方案也是傻逼
照例先手玩几组数据,发现一些结论。我们记子树的异或和表示子树中所有节点点权的异或和:
- 异或和为 0 的子树一次操作变为全 0。
- 节点数为偶数的子树一次操作后变为结论 1 中异或和为 0 的子树。节点数为奇数的子树无论进行多少次操作后异或和均不变,则我们仅需考虑对节点数为偶数的子树操作即可。
- 最后一步操作一定是对根节点操作,且操作前整棵树异或和为 0。结论 2 中对节点数为偶数的子树的操作可看做对整棵树异或该子树的异或和。
- 设依次对 \(u,v\) 两个节点进行了操作,显然 \(u,v\) 不应当具有祖先关系,否则仅需对一个节点操作即可。
问题变为:选出若干个不具有祖先关系的节点数为偶数的节点,使它们的异或和等于整棵树的异或和。
节点值域较小,考虑树上背包,对于每一个节点维护一个长度为 32 的数组 \(b_{u}\),\(b_{u,i}\) 表示将以节点 \(u\) 为根的子树的异或和变为 \(i\) 的操作方案。更新时枚举每一个子节点,将方案拼接即可。代码中自定义了一个 string 类用于存放方案。
最后输出 \(b_{1, 1}\) 即可,复杂度 \(O(32^2 \times n)\) 级别。
最后,输出方案傻逼
//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
const int kN = 2e5 + 10;
//=============================================================
int n, a[kN];
int edgenum, head[kN], v[kN << 1], ne[kN << 1];
int sz[kN], sumxor[kN];
struct my_string {
std::vector <int> s;
void init() {
s.clear();
s.push_back(0);
}
int length() {
return s.size();
}
bool empty() {
return s.empty();
}
void tag(int x_) {
s.clear();
s.push_back(x_);
}
void modify(my_string t_) {
s.clear();
for (int i = 0, lth = t_.length(); i < lth; ++ i) {
s.push_back(t_.s[i]);
}
}
void add(my_string t_) {
for (int i = 0, lth = t_.length(); i < lth; ++ i) {
s.push_back(t_.s[i]);
}
}
void print() {
for (int i = 0, lth = s.size(); i < lth; ++ i) {
printf("%d ", s[i]);
}
}
} sol[kN][32];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
void Add(int u_, int v_) {
v[++ edgenum] = v_;
ne[edgenum] = head[u_];
head[u_] = edgenum;
}
void Dfs(int u_, int fa_) {
sz[u_] = 1;
sumxor[u_] = a[u_];
for (int i = head[u_]; i; i = ne[i]) {
int v_ = v[i];
if (v_ == fa_) continue;
Dfs(v_, u_);
sz[u_] += sz[v_];
sumxor[u_] ^= sumxor[v_];
}
sol[u_][a[u_]].init();
for (int i = head[u_]; i; i = ne[i]) {
int v_ = v[i];
if (v_ == fa_) continue;
my_string tmpsol[32];
for (int j = 0; j < 32; ++ j) {
for (int k = 0; k < 32; ++ k) {
if (sol[u_][j].empty() || sol[v_][k].empty()) continue;
else if (sol[u_][j].s[0] == 0) tmpsol[j ^ k].modify(sol[v_][k]);
else if (sol[v_][k].s[0] == 0) tmpsol[j ^ k].modify(sol[u_][j]);
else tmpsol[j ^ k].modify(sol[u_][j]), tmpsol[j ^ k].add(sol[v_][k]);
}
}
for (int j = 0; j < 32; ++ j) sol[u_][j].modify(tmpsol[j]);
}
if (sz[u_] % 2 == 0) sol[u_][0].tag(u_);
// printf("-----%d\n", u_);
// for (int i = 0; i < 32; ++ i) {
// printf("%d ", i);
// std::cout << sol[u_][i] << "\n";
// }
return ;
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
n = read();
for (int i = 1; i <= n; ++ i) a[i] = read();
for (int i = 2; i <= n; ++ i) {
int v_ = read();
Add(i, v_), Add(v_, i);
}
Dfs(1, 1);
if (!sol[1][0].empty()) {
if (sol[1][0].s[0] == 0) {
printf("2\n1 1");
} else {
printf("%d\n", sol[1][0].length() + 1);
sol[1][0].print();
printf("%d ", 1);
}
} else {
printf("-1\n");
}
return 0;
}
写在最后
- 不看数据范围是傻逼。
- 不看数据范围是傻逼。
- 不看数据范围是傻逼。
- 玩少女领域却不喜欢飞鸟凑,就好像……后面忘了。