http://vjudge.net/contest/136710#overview
http://vjudge.net/contest/136710#rank
这是一场 FQ 出的练习赛,FQ 表示大多都是做过的题目。如果有哪个题目明显有印象但是没能做出来的,应该警惕。
三省吾身:传不习乎?
当然我认为此场对代码能力要求比较大,应该注意提高编程速度和准确度。
A
问一个数是否可以拆成两个正偶数的和。
puts(w % 2 == 0 && w >= 4 ? "YES" : "NO");
B
经典问题,求最大字段和。
贪心即可。
#include <bits/stdc++.h>
int main()
{
int __;
scanf("%d", &__);
for (int _ = 1; _ <= __; ++_) {
int n;
scanf("%d", &n);
int sum = 0;
int max_sum = -(1 << 30);
int begin, end, k = 1;
for (int i = 1; i <= n; ++i) {
int xi;
scanf("%d", &xi);
sum += xi;
if (sum > max_sum) {
max_sum = sum;
begin = k;
end = i;
}
if (sum < 0) {
sum = 0;
k = i + 1;
}
}
printf("Case %d:\n", _);
printf("%d %d %d\n", max_sum, begin, end);
if (_ != __) {
puts("");
}
}
return 0;
}
C
求最短路径,墙在时间是 k 的倍数的时候消失。
同一个位置按照时间模 k 的值不同有 k 种状态,照此 bfs 即可,当目前到达时间比同一状态上一次到达时间段时才有必要继续搜索。
#include <bits/stdc++.h>
struct NODE
{
int x;
int y;
int step;
NODE(int _x = 0, int _y = 0, int _step = 0)
{ x = _x; y = _y; step = _step; }
};
const int MAX_N = 123;
const int MAX_K = 15;
char str[MAX_N][MAX_N];
int flag[MAX_N][MAX_N][MAX_K];
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};
int bfs(int k, NODE you)
{
std::queue<NODE> q;
memset(flag, 0x3f, sizeof(flag));
q.push(you);
flag[you.x][you.y][0] = 0;
for (; !q.empty(); ) {
NODE now = q.front();
q.pop();
for (int i = 0; i < 4; ++i) {
NODE nxt(now.x + dx[i], now.y + dy[i], now.step + 1);
if (str[nxt.x][nxt.y] == 0) {
// out of map
continue;
}
if (nxt.step >= flag[nxt.x][nxt.y][nxt.step % k]) {
// bad move
continue;
}
if (str[nxt.x][nxt.y] == '#' && nxt.step % k) {
// there is a stone
continue;
}
q.push(nxt);
flag[nxt.x][nxt.y][nxt.step % k] = nxt.step;
if (str[nxt.x][nxt.y] == 'G') {
return nxt.step;
}
}
}
return -1;
}
int main()
{
int __;
scanf("%d", &__);
for (int _ = 1; _ <= __; ++_) {
int r, c, k;
scanf("%d %d %d", &r, &c, &k);
NODE you;
memset(str, 0, sizeof(str));
for (int i = 1; i <= r; ++i) {
scanf("%s", str[i] + 1);
for (int j = 1; j <= c; ++j) {
if (str[i][j] == 'Y') {
you.x = i;
you.y = j;
}
}
}
int answer = bfs(k, you);
if (answer != -1) {
printf("%d\n", answer);
}
else {
printf("Please give me another chance!\n");
}
}
return 0;
}
D
求可以被看到的方格序号。
方格很少,直接模拟即可。首先确定每个方格的位置 bi ,具体即是前面所有方格后接的位置的最大值。然后枚举方格,看是否被完全覆盖,具体只要注意只有大的方格会覆盖小的方格即可。
#include <bits/stdc++.h>
const int MAX_N = 100;
int b[MAX_N];
int s[MAX_N];
int main()
{
int n;
for (; scanf("%d", &n) == 1 && n; ) {
scanf("%d", s + 1);
b[1] = s[1];
for (int i = 2; i <= n; ++i) {
scanf("%d", s + i);
b[i] = 0;
for (int j = 1; j < i; ++j) {
b[i] = std::max(b[i], b[j] + std::min(s[i], s[j]) * 2);
}
}
bool first = true;
for (int i = 1; i <= n; ++i) {
int l = b[i] - s[i];
int r = b[i] + s[i];
for (int j = 1; j <= n; ++j) {
if (i == j) {
continue;
}
if (s[j] <= s[i]) {
continue;
}
int x = b[j] - s[j];
int y = b[j] + s[j];
if (x <= l && l <= y) {
l = y;
}
if (x <= r && r <= y) {
r = x;
}
}
if (l < r) {
if (first) {
first = false;
}
else {
printf(" ");
}
printf("%d", i);
}
}
printf("\n");
}
return 0;
}
E
经典问题,求源到每个点的最短路和加每个点到源的最短路之和。
前一部分可以用单源最短路的定势(dijkstra/spfa)解决;后一部分,只要把全部的边反向,再求单源最短路即可。
#include <bits/stdc++.h>
const int MAX_N = 1e6 + 10;
const int MAX_M = 2e6 + 10;
struct EDGE
{
int to;
int cost;
int next;
} edge[MAX_M];
int head[2][MAX_N];
int edge_cnt;
void reset_edge(int n)
{
edge_cnt = 0;
int* head0 = head[0];
int* head1 = head[1];
for (int i = 1; i <= n; ++i) {
head0[i] = head1[i] = -1;
}
}
void add_edge_(int u, int v, int cost, int* chead)
{
edge[edge_cnt] = (EDGE) {v, cost, chead[u]};
chead[u] = edge_cnt++;
}
void add_edge(int u, int v, int cost)
{
add_edge_(u, v, cost, head[0]);
add_edge_(v, u, cost, head[1]);
}
void sssp(int src, int* chead, long long* dis)
{
typedef std::pair<long long, int> pii;
std::priority_queue<pii> pq;
static bool done[MAX_N];
memset(done, 0, sizeof(done[0]) * MAX_N);
memset(dis, 0x3f, sizeof(dis[0]) * MAX_N);
dis[src] = 0;
pq.push(pii(0LL, src));
for (; !pq.empty(); ) {
pii p = pq.top();
pq.pop();
int u = p.second;
if (done[u]) {
continue;
}
done[u] = true;
for (int i = chead[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
int c = edge[i].cost;
if (dis[v] > c + dis[u]) {
dis[v] = c + dis[u];
pq.push(pii(-dis[v], v));
}
}
}
}
int main()
{
int __;
scanf("%d", &__);
for (int _ = 1; _ <= __; ++_) {
int n, m;
scanf("%d %d", &n, &m);
reset_edge(n);
for (int mi = 1; mi <= m; ++mi) {
int u, v, c;
scanf("%d %d %d", &u, &v, &c);
add_edge(u, v, c);
}
static long long dis[MAX_N];
long long sum = 0;
sssp(1, head[0], dis);
for (int i = 1; i <= n; ++i) {
sum += dis[i];
}
sssp(1, head[1], dis);
for (int i = 1; i <= n; ++i) {
sum += dis[i];
}
printf("%lld\n", sum);
}
return 0;
}
F
给一个数列,求有多少最大值减最小值不超过 k 的区间。
几乎是单调队列的模板问题。当然也可以用支持快速查询区间最值的数据结构解决。
这里给出稀疏表的写法。枚举左端点,二分右端点——可以延伸到的最远位置。
#include <bits/stdc++.h>
const int MAX_N = 1e5 + 10;
const int MAX_LOG_N = 20;
int a[MAX_N];
int stmax[MAX_LOG_N][MAX_N];
int stmin[MAX_LOG_N][MAX_N];
int uplog[MAX_N];
void initst(int n)
{
int* now_max = stmax[0];
int* now_min = stmin[0];
int* pre_max;
int* pre_min;
for (int i = 1; i <= n; ++i) {
now_max[i] = now_min[i] = a[i];
}
for (int j = 1, pj = 2; pj <= n; ++j, pj <<= 1) {
now_max = stmax[j];
pre_max = stmax[j - 1];
now_min = stmin[j];
pre_min = stmin[j - 1];
int hpj = pj / 2;
for (int i = 1; i + pj - 1 <= n; ++i) {
now_max[i] = std::max(pre_max[i], pre_max[i + hpj]);
now_min[i] = std::min(pre_min[i], pre_min[i + hpj]);
}
}
uplog[1] = 0;
for (int i = 2; i <= n; ++i) {
uplog[i] = uplog[i >> 1] + 1;
}
}
int rmaxq(int l, int r)
{
int p = uplog[r - l + 1];
return std::max(stmax[p][l], stmax[p][r - (1 << p) + 1]);
}
int rminq(int l, int r)
{
int p = uplog[r - l + 1];
return std::min(stmin[p][l], stmin[p][r - (1 << p) + 1]);
}
int extend(int i, int n, int k)
{
if (rmaxq(i, n) - rminq(i, n) < k) {
return n;
}
int low = i;
int upp = n;
for (; upp - low > 1; ) {
int mid = (low + upp) >> 1;
if (rmaxq(i, mid) - rminq(i, mid) < k) {
low = mid;
}
else {
upp = mid;
}
}
return low;
}
int main()
{
int __;
scanf("%d", &__);
for (int _ = 1; _ <= __; ++_) {
int n, k;
scanf("%d %d", &n, &k);
for (int i = 1; i <= n; ++i) {
scanf("%d", a + i);
}
initst(n);
long long answer = 0;
for (int i = 1; i <= n; ++i) {
answer += extend(i, n, k) - i + 1;
}
printf("%lld\n", answer);
}
return 0;
}
posted by 张静之