AtCoder Beginner Contest 359 补题记录(A~E,G,G 暂无代码)
A
直接统计 Takahashi
出现次数即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2000100;
int a[N];
signed main() {
int n;
cin >> n;
int cnt = 0;
while (n--) {
string s;
cin >> s;
if (s == "Takahashi") cnt++;
}
cout << cnt << '\n';
return 0;
}
B
直接记录每种衣服的两个人出现的位置即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2000100;
int a[N];
vector<int> scc[N];
signed main() {
int n;
cin >> n;
n <<= 1;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
scc[a[i]].push_back(i);
int cnt = 0;
for (int i = 1; i <= n / 2; i++)
if (abs(scc[i][1] - scc[i][0]) == 2)
cnt++;
cout << cnt << '\n';
return 0;
}
C
从这里开始变的 \(\text{inv\_sky}\) 了。
首先纵向的走是没有办法省事的,距离是多长就需要走多少步才能移动到下一条直线上。
横着走的话,首先在同一个 \(\text{tiles}\) 中肯定先走到最靠近对方的地方。因此如果横坐标的差 \(\le 1\) 则直接特判为纵坐标距离,其他情况则直接启动计算几何找出两条对角线和两条平行于坐标轴的直线,将平面分为八个区域之后根据八个区域来分类讨论求答案即可。但是赛时代码实际因为做了一些优化而简单的多(代码量不是一个级别的)。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2000100;
int a[N];
vector<int> scc[N];
signed main() {
int sx, sy, tx, ty;
cin >> sx >> sy >> tx >> ty;
if (sy == ty && sx / 2 != tx / 2 && abs(sx / 2 - tx / 2) == 1) cout << "0\n";
else {
if (sx == tx || abs(sx - tx) == 1)
cout << abs(sy - ty) << '\n';
else {
int len = abs(sy - ty);
if (sx > tx) {
if ((sx + sy) & 1) sx--;
if (~(tx + ty) & 1) tx++;
} else {
if (~(sx + sy) & 1) sx++;
if ((tx + ty) & 1) tx--;
}
int heng = abs(sx - tx);
int len2 = max(len, heng) - len;
len2 = (len2 + 1) / 2;
// cout << "dbg " << len << ' ' << len2 << '\n';
cout << len + len2 << '\n';
}
}
return 0;
}
D
大 dp。设 \(f_{i,j}\) 表示前 \(i\) 个数,当前后 \(k\) 位(如果没有 \(k\) 位则以 \(0\) 来补充)的字符串状态,方案数 \(\bmod 998244353\) 是多少。那么 ?
的情况就是把 A
和 B
的情况分别跑一遍。A
和 B
情况基本等价,都是暴力枚举上一个状态,然后计算出当前的状态,判断是否当前状态或者上一个状态回文,这个因为长度 \(\le 10\) 所以暴力判断即可。直接硬暴力 dp 时间复杂度就是对的。细节十分多。附上 5.5K
的代码。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 4110, mod = 998244353;
int f[N][N];
bool check(string s) {
string t = s;
reverse(t.begin(), t.end());
return s == t;
// return 0;
}
int n, k;
string s;
string dogs(int x) {
string t;
for (int i = 0; i <= k; i++)
t = t + (char)(x % 2 + '0'), x /= 2;
reverse(t.begin(), t.end());
return t;
}
signed main() {
cin >> n >> k >> s;
s = ' ' + s;
// while (true) {
// int s;
// cin >> s;
// cout << dogs(s) << '\n';
// }
f[0][0] = 1;
for (int i = 1; i <= n; i++) {
if (s[i] == 'A' || s[i] == 'B') {
if (i <= k) {
for (int j = 0; j < (1ll << k); j++)
if (f[i - 1][j]) {
int k1 = j, k2 = j * 2 + s[i] - 'A';
// 从k1转移到k2
string now;
int k22 = k2;
for (int p = 1; p <= i; p++) {
now = now + (char)(k22 % 2 + 'A');
k22 /= 2;
}
if (i < k || !check(now))
f[i][k2] = (f[i][k2] + f[i - 1][k1]) % mod;
}
} else {
for (int j = 0; j < (1ll << k); j++) {
if (f[i - 1][j]) {
int k1 = j, k2 = j * 2 + s[i] - 'A';
// 取出 k2 的最高位
string tok2 = dogs(k2);
k2 = 0;
for (int i = 1; i < tok2.size(); i++)
k2 = k2 * 2 + tok2[i] - '0';
string now;
int k22 = k2;
for (int p = 1; p <= k; p++) {
now = now + (char)(k22 % 2 + 'A');
k22 /= 2;
}
if (!check(now))
f[i][k2] = (f[i][k2] + f[i - 1][k1]) % mod;
}
}
}
} else {
s[i] = 'A';
if (i <= k) {
for (int j = 0; j < (1ll << k); j++)
if (f[i - 1][j]) {
int k1 = j, k2 = j * 2 + s[i] - 'A';
// 从k1转移到k2
string now;
int k22 = k2;
for (int p = 1; p <= i; p++) {
now = now + (char)(k22 % 2 + 'A');
k22 /= 2;
}
if (i < k || !check(now))
f[i][k2] = (f[i][k2] + f[i - 1][k1]) % mod;
}
} else {
for (int j = 0; j < (1ll << k); j++) {
if (f[i - 1][j]) {
int k1 = j, k2 = j * 2 + s[i] - 'A';
string tok2 = dogs(k2);
k2 = 0;
for (int i = 1; i < tok2.size(); i++)
k2 = k2 * 2 + tok2[i] - '0';
string now;
int k22 = k2;
for (int p = 1; p <= k; p++) {
now = now + (char)(k22 % 2 + 'A');
k22 /= 2;
}
if (!check(now))
f[i][k2] = (f[i][k2] + f[i - 1][k1]) % mod;
// cout << "to " << j << ' ' << k2 << '\n';
}
}
}
s[i] = 'B';
if (i <= k) {
for (int j = 0; j < (1ll << k); j++)
if (f[i - 1][j]) {
int k1 = j, k2 = j * 2 + s[i] - 'A';
// 从k1转移到k2
// cout << "to " << k1 << ' ' << k2 << '\n';
string now;
int k22 = k2;
for (int p = 1; p <= i; p++) {
now = now + (char)(k22 % 2 + 'A');
k22 /= 2;
}
if (i < k || !check(now))
f[i][k2] = (f[i][k2] + f[i - 1][k1]) % mod;
}
} else {
for (int j = 0; j < (1ll << k); j++) {
if (f[i - 1][j]) {
int k1 = j, k2 = j * 2 + s[i] - 'A';
string tok2 = dogs(k2);
k2 = 0;
for (int i = 1; i < tok2.size(); i++)
k2 = k2 * 2 + tok2[i] - '0';
// cout << "dbg " << i << ' ' << j << ' ' << k1 << ' ' << k2 << '\n';
string now;
int k22 = k2;
for (int p = 1; p <= k; p++) {
now = now + (char)(k22 % 2 + 'A');
k22 /= 2;
}
if (!check(now))
f[i][k2] = (f[i][k2] + f[i - 1][k1]) % mod;
}
}
}
s[i] = '?';
}
// cout << "dbg : ";
// for (int j = 0; j < (1ll << k); j++) cout << f[i][j] << ' ';
// cout << '\n';
}
int ss = 0;
for (int i = 0; i < (1ll << k); i++)
ss = (ss + f[n][i]) % mod;
cout << ss << '\n';
return 0;
}
E
令 \(p_i\) 表示 \(i\) 点左边第一个比 \(i\) 柱子高的柱子的编号。显然 \(p_i\sim i\) 号柱子之间的所有水槽都注满水,那么水才能蔓延到 \(i\) 号柱子右边。\(p_i\) 可以二分 + ST 表求解,灌水的操作可以使用懒标记线段上来维护。线段树维护一段区间的总水量,只需要将 \(p_i\sim i\) 中全部的水槽全部区间推平为同等高度即可。因为只有区间推平所以也可以使用 ODT 来维护。总的时间复杂度为 \(O(n\log n)\)。如果 \(p_i\) 用二分套线段树则是 \(O(n\log^2n)\) 的。
/*
所有左边比她严格小的棍子隔开的格子都得灌满水才行
*/
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500100, mod = 998244353;
int a[N], h[N], id[N];
int f[N][20];
int lg[N],n,m;
void init(){
lg[0]=-1;
for(int i=1;i<=n;i++)
lg[i]=lg[i>>1]+1,f[i][0]=h[i];
for(int j=1;j<20;j++)
for(int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=max(f[i+(1<<(j-1))][j-1],f[i][j-1]);
}
int query(int l,int r){
if(l>r)
return 0;
int len=r-l+1,k=lg[len];
return max(f[l][k],f[r-(1<<k)+1][k]);
}
struct Node {
int l, r, sum, tag;
void init(int p) {
l = r = p;
tag = 0, sum = 0;
}
void co(int x) {
sum = (r - l + 1) * x;
tag = x;
}
} z[N << 2];
Node operator+(const Node &l, const Node &r) {
Node res;
res.l = l.l, res.r = r.r;
res.sum = l.sum + r.sum, res.tag = 0;
return res;
}
void build(int l, int r, int rt) {
if (l == r) return z[rt].init(l);
int mid = l + r >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
void pud(int rt) {
if (z[rt].tag) {
z[rt << 1].co(z[rt].tag);
z[rt << 1 | 1].co(z[rt].tag);
z[rt].tag = 0;
}
}
void modify(int l, int r, int rt, int ll, int rr, int v) {
if (ll <= l && r <= rr) return z[rt].co(v);
int mid = l + r >> 1; pud(rt);
if (ll <= mid) modify(l, mid, rt << 1, ll, rr, v);
if (mid < rr) modify(mid + 1, r, rt << 1 | 1, ll, rr, v);
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
signed main() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> h[i];
id[1] = 1;
init();
for (int i = 2; i <= n + 1; i++) {
int l = 1, r = i - 1, best = i;
while (l <= r) {
int mid = l + r >> 1;
if (h[i] >= query(mid, i))
best = mid, r = mid - 1;
else
l = mid + 1;
}
id[i] = best;
}
// cout << "dbg ";
// for (int i = 1; i <= n + 1; i++) cout << id[i] << ' ';
// cout << '\n';
// id[i] 表示 i 想要灌水最左边需要加水的格子是哪一个
build(1, n + 1, 1);
for (int i = 1; i <= n; i++) {
modify(1, n + 1, 1, id[i], i, h[i]);
cout << 1 + z[1].sum << ' ';
}
cout << '\n';
return 0;
}
G
沿用 CF176E(希望我没有记错题号)的思路,对于每一个不同的颜色建立虚树来维护,则问题转化为求树上任意两点之间简单路径的长度之和。这是平凡的。其实也可以使用点分治来维护,但是忘记了点分治怎么写了。但是还有各种其他神秘做法。研究懂了再补充。
代码还没调完,其实是因为赛时没写完然后懒得调了。
本文来自博客园,作者:yhbqwq,转载请注明原文链接:https://www.cnblogs.com/yhbqwq/p/18262823,谢谢QwQ