20240407
T1
Topcoder SRM 583 div1 Medium - TurnOnLamps
发现取反一条路径相当于把两个端点到根的路径分别取反。所以只要考虑到根的情况。设 \(f[i]\) 表示 \(i\) 子树内所有边都合法的最小操作次数,则每个点只要把所有儿子的 \(f\) 加过来然后看到儿子的每条边是否合法即可。最后要除以 \(2\) 上取整。
代码
#include <iostream>
using namespace std;
string ini, imp;
int head[55], nxt[105], to[105], ecnt = -1;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int f[55];
void dfs(int x, int fa) {
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if (v != fa) {
dfs(v, x);
f[x] += f[v];
f[x] += ((imp[i >> 1] != '0') && (((f[v] + ini[i >> 1] - '0') & 1) == 0));
}
}
}
int main() {
int n;
cin >> n;
++n;
for (int i = 2; i <= n; i++) {
int x;
cin >> x;
add(i, x + 1);
add(x + 1, i);
}
cin >> ini >> imp;
dfs(1, 0);
cout << (f[1] >> 1) + (f[1] & 1) << "\n";
return 0;
}
T2
Topcoder SRM 590 div1 Medium - XorCards
首先找有多少种不同的异或结果在 \(m\) 之内。考虑使用线性基。先把所有线性基消成每一列至多只有一个 \(1\) 的形式,然后二分一下求第 \(k\) 小异或值。这些值都是可以被表出的。接下来考虑那些没有成功插入线性基的数,这些数一定都可以被线性基中的数表出。所以对于任意一种这些数的选择方案,一定可以通过选择线性基中对应的能够表出这些数的数来让当前的异或和不变,从而对于每一种可行的异或值与每一种选择没有成功插入线性基的数的选择方案的组合都能够找到一种可行的选择若干数的合法方案。所以只需要把两个乘一下即可。
代码
#include <iostream>
#define int long long
using namespace std;
int n, lim;
int a[56, x[56, p[56;
int cnt;
void Insert(int v) {
for (int i = 55; ~i; i--) {
if (v & (1ll << i)) {
if (x[i])
v ^= x[i];
else {
x[i] = v;
return;
}
}
}
++cnt;
}
int pcnt;
void Deal() {
for (int i = 0; i <= 55; i++) {
for (int j = i - 1; ~j; j--) {
if (x[i] & (1ll << j))
x[i] ^= x[j];
}
}
for (int i = 0; i <= 55; i++) {
if (x[i])
p[pcnt++] = x[i];
}
}
int kth(int k) {
int ret = 0;
for (int i = 0; i <= 55; i++) {
if (k & (1ll << i))
ret ^= p[i];
}
return ret;
}
signed main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i], Insert(a[i]);
cin >> lim;
Deal();
int l = 0, r = (1ll << pcnt) - 1, mid, ans = 0;
while (l <= r) {
mid = (l + r) >> 1;
if (kth(mid) <= lim)
ans = mid, l = mid + 1;
else
r = mid - 1;
}
cout << ((ans + 1) << cnt) << "\n";
return 0;
}
T3
Topcoder SRM 595 div1 Medium - LittleElephantAndRGB
考虑所有可行情况,分别是 可行 + 可行,可行 + 不可,不可 + 可行 和 不可 + 不可。我们先枚举第一个串的开头和结束位置,然后判断是否可行。如果可行就加上后面所有子串个数,否则考虑最后有没有 \(\texttt{G}\)。如果有,设最后有 \(x\) 个连续的,就加上后面所有可行子串个数和不可行但是前面有至少 \(lim - x\) 个连续 \(\texttt{G}\) 的子串数。如果最后没有,就只加上后面所有可行的。这样就要两个数组:\(f[i][j]\) 表示开始位置在 \(i\) 及以后且开头有至少 \(j\) 个连续 \(\texttt{G}\) 的不合法子串的个数,\(g[i]\) 表示开始位置在 \(i\) 及以后的合法子串数。这两个都是很好算的,随便搞搞就好了。时间复杂度 \(\mathcal{O}(n^2)\)。
代码
#include <iostream>
#define int long long
using namespace std;
int n, lim;
string str;
int f[2505][2505]; // begin >= i, have at lease j consective 'g's in the beginnning, not ok
int g[2505]; // count of valid strings whose start >= i
bool ok[2505][2505]; // whether str[i, j] is ok
signed main() {
cin >> n;
while (n--) {
string s;
cin >> s;
str += s;
}
n = str.size();
cin >> lim;
str = ' ' + str;
int ans = 0;
for (int i = 1; i <= n; i++) {
int cg = 0;
for (int j = i; j <= n; j++) {
if (str[j] == 'G')
cg++;
else
cg = 0;
ok[i][j] = (ok[i][j - 1] | (cg >= lim));
g[i] += ok[i][j];
}
}
for (int i = n; i; i--) g[i] += g[i + 1];
for (int i = 1; i <= n; i++) {
int j = i;
while (str[j] == 'G' && j - i + 1 < lim) f[i][j - i + 1] = 1, ++j;
int cnt = j - i;
for (; j <= n; j++) f[i][cnt] += !ok[i][j];
for (int j = n; j; j--)
f[i][j] += f[i][j + 1];
if (str[j] == 'G' && j - i + 1 == lim)
continue;
}
for (int i = n; i; i--) {
for (int j = 1; j <= n; j++)
f[i][j] += f[i + 1][j];
}
for (int i = 1; i < n; i++) {
int cg = 0;
for (int j = i; j < n; j++) {
int tmp = ans;
if (str[j] == 'G')
cg++;
else
cg = 0;
if (ok[i][j])
ans += (n - j) * (n - j + 1) / 2;
else if (str[j] != 'G')
ans += g[j + 1];
else
ans += f[j + 1][lim - cg] + g[j + 1];
}
}
cout << ans << "\n";
return 0;
}
T4
Topcoder SRM 595 div1 Medium - ConvexPolygonGame
可以发现当且仅当多边形内顶点全部共线时先手必败,否则先手必胜。所以只需要判断多边形内顶点是否全部共线。发现一行中最多 \(2 \times 10^5\) 个点,所以如果多边形内有超过这么多点就必有不贡献的。接下来由于点数很少,只需要枚举所有点并且判断是否与前两个点共线即可。
代码
#include <iostream>
#include <math.h>
#define int long long
using namespace std;
const int inf = 2147483647;
struct Point {
int x, y;
} a[55], x[300005];
struct Line {
double k, b;
};
int cnt;
Line calc(Point a, Point b) {
Line ret;
ret.k = (1.0 * b.y - a.y) / (b.x - a.x);
ret.b = a.y - ret.k * a.x;
return ret;
}
double calc(int x, Line l) { return l.k * x + l.b; }
double calc(int x, Point a, Point b) { return calc(x, calc(a, b)); }
bool inLine(Point a, Point b, Point c) { return (c.y - b.y) * (b.x - a.x) == (b.y - a.y) * (c.x - b.x); }
signed main() {
signed n;
cin >> n;
for (int i = 1; i <= n; i++) {
signed adsf;
cin >> adsf;
a[i].x = adsf + 100000;
}
cin >> n;
for (int i = 1; i <= n; i++) {
signed asdf;
cin >> asdf;
a[i].y = asdf;
}
a[n + 1].x = a[1].x, a[n + 1].y = a[1].y;
bool ok = 1;
for (int i = 0; i <= 200000; i++) {
double yy1 = inf, yy2 = inf;
bool v1 = 0, v2 = 0;
for (int j = 1; j <= n; j++) {
if ((a[j].x < i && i < a[j + 1].x) || (a[j + 1].x < i && i < a[j].x)) {
if (yy1 != inf)
yy2 = calc(i, a[j], a[j + 1]);
else
yy1 = calc(i, a[j], a[j + 1]);
}
if (a[j].x == i) {
if (yy1 != inf)
yy2 = a[j].y, v2 = 1;
else
yy1 = a[j].y, v1 = 1;
}
}
if (yy1 == inf || yy2 == inf)
continue;
if (yy1 > yy2)
swap(yy1, yy2), swap(v1, v2);
int y1 = ceil(yy1), y2 = floor(yy2);
y1 += v1, y2 -= v2;
for (int j = y1; j <= y2; j++) {
x[++cnt] = (Point) { i, j };
if (cnt >= 3)
ok &= inLine(x[1], x[2], x[cnt]);
if (cnt >= 200001) {
ok = 0;
break;
}
}
}
if (!(cnt <= 2 || ok))
cout << "Masha\n";
else
cout << "Petya\n";
return 0;
}
T5
Topcoder SRM 416 div1 Hard - RussianCheckers
直接爆搜模拟即可。注意细节,诸如如何判断王能否吃子一类。
代码
#include <iostream>
#include <algorithm>
#include <string.h>
#include <vector>
using namespace std;
#define WHITE_KING 1
#define WHITE_PAWN 2
#define BLACK_KING 3
#define BLACK_PAWN 4
#define BARRIER 5
int n;
int a[10][10];
int rec[10][10];
int rrec[10][10];
struct Position {
int x, y;
};
string curclr;
bool isMine(int i, int j) {
return (curclr == "BLACK" && a[i][j] >= 3 && a[i][j] <= 4)
|| (curclr == "WHITE" && a[i][j] >= 1 && a[i][j] < 3);
}
bool isOpp(int x, int y) { return !isMine(x, y) && a[x][y] != 0 && a[x][y] != 5; }
int curpos;
vector<string> ans;
int Line;
bool Empty(int x, int y) {
return (x >= 1 && y >= 1 && x <= 8 && y <= 8 && a[x][y] == 0);
}
bool MoveKing(int x, int y);
void moveKing(int x, int y);
bool MovePawn(int x, int y);
void movePawn(int x, int y);
int dx[4] = { 1, -1, 1, -1 };
int dy[4] = { -1, 1, 1, -1 };
string curstr;
string trans(int x, int y) {
string a = "";
a += (x + 'a' - 1);
a += (y + '0');
return a;
}
bool MovePawn(int x, int y) {
if (y == Line)
return MoveKing(x, y);
bool Capture = 0;
for (int i = 0; i < 4; i++) {
if (Empty(x + dx[i] * 2, y + dy[i] * 2) && isOpp(x + dx[i], y + dy[i])) {
a[x + dx[i]][y + dy[i]] = BARRIER;
curstr += ":";
curstr += trans(x + 2 * dx[i], y + 2 * dy[i]);
Capture = 1;
if (!MovePawn(x + 2 * dx[i], y + 2 * dy[i]))
ans.emplace_back(curstr);
a[x + dx[i]][y + dy[i]] = rec[x + dx[i]][y + dy[i]];
curstr.pop_back();
curstr.pop_back();
curstr.pop_back();
}
}
return Capture;
}
void movePawn(int x, int y) {
if (curclr == "BLACK") {
if (Empty(x - 1, y - 1))
ans.emplace_back(trans(x, y) + "-" + trans(x - 1, y - 1));
if (Empty(x + 1, y - 1))
ans.emplace_back(trans(x, y) + "-" + trans(x + 1, y - 1));
} else {
if (Empty(x - 1, y + 1))
ans.emplace_back(trans(x, y) + "-" + trans(x - 1, y + 1));
if (Empty(x + 1, y + 1))
ans.emplace_back(trans(x, y) + "-" + trans(x + 1, y + 1));
}
}
vector<Position> CaptureInDir(int x, int y, int d);
bool CanCaptureInDir(int x, int y, int d);
bool CanCaptureWithoutDir(int x, int y, int d);
bool CanCapture(int x, int y);
vector<Position> CaptureInDir(int x, int y, int d) {
vector<Position> tmp;
bool haveO = 0;
bool ok = 0;
for (int i = 1; i <= 8; i++) {
int cx = x + dx[d] * i, cy = y + dy[d] * i;
if (haveO) {
if (Empty(cx, cy))
ok |= CanCaptureWithoutDir(cx, cy, d ^ 1);
else
break;
} else {
if (isOpp(cx, cy))
haveO = 1;
else if (!Empty(cx, cy))
break;
}
}
haveO = 0;
for (int i = 1; i <= 8; i++) {
int cx = x + dx[d] * i, cy = y + dy[d] * i;
if (haveO) {
if (Empty(cx, cy)) {
if (ok == CanCaptureWithoutDir(cx, cy, d ^ 1))
tmp.emplace_back((Position) { cx, cy });
} else
break;
} else {
if (isOpp(cx, cy))
haveO = 1;
else if (!Empty(cx, cy))
break;
}
}
return tmp;
}
bool CanCaptureInDir(int x, int y, int d) {
bool ret = 0;
bool haveO = 0;
for (int i = 1; i <= 8; i++) {
int cx = x + dx[d] * i, cy = y + dy[d] * i;
if (isOpp(cx, cy)) {
if (Empty(cx + dx[d], cy + dy[d]))
return 1;
else
return 0;
} else if (!Empty(cx, cy))
return 0;
}
return 0;
}
bool CanCaptureWithoutDir(int x, int y, int d) {
bool ret = 0;
for (int i = 0; i < 4; i++) {
if (i == d)
continue;
ret |= CanCaptureInDir(x, y, i);
}
return ret;
}
bool CanCapture(int x, int y) {
bool ret = 0;
for (int i = 0; i < 4; i++) ret |= CanCaptureInDir(x, y, i);
return ret;
}
void SetBarrier(int sx, int sy, int tx, int ty, int d) {
for (;; sx += dx[d], sy += dy[d]) {
if (a[sx][sy] != 0)
a[sx][sy] = BARRIER;
if (sx == tx)
return;
}
}
void Clear(int sx, int sy, int tx, int ty, int d) {
for (;; sx += dx[d], sy += dy[d]) {
a[sx][sy] = rec[sx][sy];
if (sx == tx)
return;
}
}
bool MoveKing(int x, int y) {
vector<Position> vec;
if (!CanCapture(x, y))
return 0;
bool ret = 0;
for (int i = 0; i < 4; i++) {
if (CanCaptureInDir(x, y, i)) {
ret = 1;
vec = CaptureInDir(x, y, i);
for (auto v : vec) {
curstr += ":";
curstr += trans(v.x, v.y);
SetBarrier(x + dx[i], y + dy[i], v.x, v.y, i);
if (!MoveKing(v.x, v.y))
ans.emplace_back(curstr);
curstr.pop_back();
curstr.pop_back();
curstr.pop_back();
Clear(x + dx[i], y + dy[i], v.x, v.y, i);
}
}
}
return ret;
}
void moveKing(int x, int y) {
for (int i = 0; i < 4; i++) {
for (int j = 1; j <= 8; j++) {
int cx = x + dx[i] * j;
int cy = y + dy[i] * j;
if (!Empty(cx, cy))
break;
ans.emplace_back(trans(x, y) + "-" + trans(cx, cy));
}
}
}
int main() {
cin >> n;
for (int i = n; i; i--) {
string str;
cin >> str;
for (int j = 1; j <= n; j++) {
if (str[j - 1] == 'w')
a[j][i] = WHITE_PAWN;
else if (str[j - 1] == 'b')
a[j][i] = BLACK_PAWN;
else if (str[j - 1] == 'W')
a[j][i] = WHITE_KING;
else if (str[j - 1] == 'B')
a[j][i] = BLACK_KING;
}
}
cin >> curclr;
if (curclr == "BLACK")
Line = 1;
else
Line = 8;
memcpy(rec, a, sizeof a);
memcpy(rrec, a, sizeof a);
bool eat = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (isMine(i, j)) {
memcpy(a, rrec, sizeof rrec);
memcpy(rec, rrec, sizeof rrec);
rec[i][j] = a[i][j] = 0;
curstr = trans(i, j);
if (rrec[i][j] & 1) {
if (!MoveKing(i, j))
moveKing(i, j);
else
eat = 1;
} else {
if (!MovePawn(i, j))
movePawn(i, j);
}
}
}
}
sort(ans.begin(), ans.end());
for (int i = 0; i < (int)ans.size(); i++) eat |= (ans[i][2] == ':');
for (int i = 0; i < (int)ans.size(); i++) {
if (!eat) {
if (i + 1 == (int)ans.size())
cout << ans[i];
else
cout << ans[i] << "\n";
} else {
if (ans[i][2] == ':') {
if (i + 1 == (int)ans.size())
cout << ans[i];
else
cout << ans[i] << "\n";
}
}
}
return 0;
}
某些树上路径操作可以变成到根路径操作
异或问题可以考虑线性基。
大胆猜结论。
写代码时尽量想清细节

浙公网安备 33010602011771号