「USACO2011 Nov Gold」解题报告
「USACO2011 Nov Gold」解题报告
T1、Above the Median
\(Description\):
给出一个长度为 \(n \ (n \leq 10 ^ 5)\) 序列 \(\{ a_n \} \ (a_i \leq 10 ^ 9)\),问中位数大于等于 \(k\) 的区间数量。(区间里如果有偶数个数,中位数定义为偏大的那一个)
\(Solution\):
若把 \(a_i \geq k\) 的位置标为 \(1\),\(a_i < k\) 标为 \(0\),并把这个序列记为 \(b\),该序列前缀和记为 \(pre\);
则区间 \([l,r]\) 满足题意当且仅当:
\[r - (l - 1) \leq 2 \times (pre_r - pre_{l - 1})
\]
把变量相同的放一边:
\[r - 2 \times pre_r \leq (l - 1) - 2 \times pre_{l - 1}
\]
枚举右端点,随便找个数据结构维护左边有几个满足条件的左端点。
\(Source\):
#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
int x = 0; char c = getchar(); bool f = 0;
while (c < '0' || c > '9')
f |= c == '-', c = getchar();
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
const int N = 1e5 + 5;
int n, nn, k, a[N], pre[N];
long long res;
struct binary_index_tree {
int a[N << 1];
void insert(int p) { for (; p <= nn; p += (p & -p)) ++a[p]; }
int ask(int p) { int ret = 0; for (; p; p -= (p & -p)) ret += a[p]; return ret; }
} bit;
int main() {
//freopen("in", "r", stdin);
n = in(), k = in(), nn = n + n;
for (int i = 1; i <= n; ++i)
a[i] = in();
for (int i = 1; i <= n; ++i)
pre[i] = pre[i - 1] + (a[i] >= k);
bit.insert(n);
for (int i = 1; i <= n; ++i) {
res += bit.ask(pre[i] + pre[i] - i + n);
bit.insert(pre[i] + pre[i] - i + n);
}
printf("%lld\n", res);
return 0;
}
T2、Binary Sudoku
\(Description\):
给出一个 \(9 \times 9\) 的 \(01\) 矩阵,问最少修改几个数能使每行、每列以及每个九宫格中的异或和为 \(0\)。
\(Solution\):
\(f_{k, i, x, y, z}\) 表示到第 \(k\) 行,其中 \(1\) 至 \(k\) 行每一列异或和的状态为 \(i\) (一个二进制数),当前的三个宫格异或和分别是 \(x,y,z\) 的最少操作次数;
转移枚举第 \(k\) 行的操作;
注意每个九宫格的最后一行 \(x,y,z\) 都是 \(0\)。
\(Source\):
#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
int x = 0; char c = getchar(); bool f = 0;
while (c < '0' || c > '9')
f |= c == '-', c = getchar();
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
int a[11][4], b[515][4], f[11][515][2][2][2];
char str[15];
int main() {
//freopen("in", "r", stdin);
for (int i = 1; i <= 9; ++i) {
scanf(" %s", str + 1);
for (int j = 1; j <= 9; ++j)
a[i][0] = (a[i][0] << 1) | (str[j] == '1');
for (int j = 0; j < 9; ++j)
a[i][j / 3 + 1] ^= (a[i][0] >> j) & 1;
}
for (int i = 1; i < 512; ++i) {
b[i][0] = b[i - (i & -i)][0] + 1;
for (int j = 0; j < 9; ++j)
b[i][j / 3 + 1] ^= (i >> j) & 1;
}
memset(f, 0x3f, sizeof(f));
f[0][0][0][0][0] = 0;
for (int k = 1, tmp, t1, t2, t3; k <= 9; ++k) {
if (k % 3 == 0) {
for (int i = 0; i < 512; ++i)
for (int j = 0; j < 512; ++j) {
if (b[j ^ a[k][0]][0] & 1)
continue;
t1 = a[k][1] ^ b[j][1];
t2 = a[k][2] ^ b[j][2];
t3 = a[k][3] ^ b[j][3];
tmp = f[k - 1][i ^ j ^ a[k][0]][t1][t2][t3];
chk_min(f[k][i][0][0][0], tmp + b[j][0]);
}
} else {
for (int i = 0; i < 512; ++i)
for (int x = 0; x < 2; ++x)
for (int y = 0; y < 2; ++y)
for (int z = 0; z < 2; ++z)
for (int j = 0; j < 512; ++j) {
if (b[j ^ a[k][0]][0] & 1)
continue;
t1 = x ^ a[k][1] ^ b[j][1];
t2 = y ^ a[k][2] ^ b[j][2];
t3 = z ^ a[k][3] ^ b[j][3];
tmp = f[k - 1][i ^ j ^ a[k][0]][t1][t2][t3];
chk_min(f[k][i][x][y][z], tmp + b[j][0]);
}
}
}
printf("%d\n", f[9][0][0][0][0]);
return 0;
}
T3、Cow Steeplechase
\(Description\):
给出 \(n \ (n \leq 250)\) 平行于坐标轴的线段,选出尽量多的线段使得这些线段两两没有交点 (顶点也算) ,横的与横的,竖的与竖的线段之间保证没有交点,输出最多能选出多少条线段。
坐标均大于 \(0\),且不超过 \(10 ^ 9\)。
\(Solution\):
二分图最大独立集。
\(Source\):
#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
int x = 0; char c = getchar(); bool f = 0;
while (c < '0' || c > '9')
f |= c == '-', c = getchar();
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
const int N = 255;
struct segment {
int x1, y1, x2, y2;
} a[N], b[N];
int n, x, y, mp[N][N], mat[N];
void prep() {
for (int i = 1; i <= x; ++i)
if (a[i].x1 > a[i].x2)
std::swap(a[i].x1, a[i].x2);
for (int i = 1; i <= y; ++i)
if (b[i].y1 > b[i].y2)
std::swap(b[i].y1, b[i].y2);
for (int i = 1; i <= x; ++i) {
for (int j = 1; j <= y; ++j) {
if (b[j].y1 <= a[i].y1 && a[i].y2 <= b[j].y2 &&
a[i].x1 <= b[j].x1 && b[j].x2 <= a[i].x2)
mp[i][j] = 1;
}
}
}
int vis[N];
int dfs(const int u, const int tim) {
if (vis[u] == tim)
return 0;
vis[u] = tim;
for (int v = 1; v <= y; ++v) {
if (!mp[u][v])
continue;
if (!mat[v] || dfs(mat[v], tim)) {
mat[v] = u;
return 1;
}
}
return 0;
}
int hungary() {
int ret = 0;
for (int i = 1; i <= x; ++i)
ret += dfs(i, i);
return ret;
}
int main() {
//freopen("in", "r", stdin);
n = in();
for (int i = 1; i <= n; ++i) {
a[++x] = (segment){in(), in(), in(), in()};
if (a[x].x1 == a[x].x2)
b[++y] = a[x--];
}
prep();
printf("%d\n", n - hungary());
return 0;
}