1001 追星族
题目大意
平面内有N个点,Ti时刻在(Xi, Yi)点有演出
在0时刻度度熊从任意起点出发,移动速度为1
设计路线使得在所有的演出时刻,它距离演出地点距离的最大值最小
答案输出分数形式
算法思路
平面上的曼哈顿距离,可以通过坐标转换,变成1维数轴上的问题
即两点之间的曼哈顿距离等于两点在x + y数轴空间和x - y数轴空间上距离的最大值
对于本题,可以二分一个答案,然后判断在两个数轴上是否同时可行
按照时间排序后,维护区间的交集
每次将原区间向左右扩展可以移动的距离,再与当前坐标两侧的可行区域求交
判定条件为最后的区域是否为空
由于可行区域的端点可能位于两个整点之间,所以事先将所有坐标乘2,最后根据奇偶输出
时间复杂度: O(NlogN)
代码
/**
* Copyright © 2015 Authors. All rights reserved.
*
* FileName: A.cpp
* Author: Beiyu Li <sysulby@gmail.com>
* Date: 2015-06-09
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
typedef long long LL;
typedef pair<int, int> Pii;
const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;
const int maxn = 50000 + 5;
int n;
struct Point {
int t;
LL x[2];
bool operator<(const Point &p) const { return t < p.t; }
} a[maxn];
bool check(LL d)
{
rep(o,2) {
LL l = a[0].x[o] - d, r = a[0].x[o] + d;
for (int i = 1; i < n; ++i) {
LL dt = a[i].t - a[i-1].t;
l = max(l - dt * 2, a[i].x[o] - d);
r = min(r + dt * 2, a[i].x[o] + d);
if (l > r) return false;
}
}
return true;
}
int main()
{
int T, cas = 0;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
rep(i,n) {
int x, y;
scanf("%d%d%d", &x, &y, &a[i].t);
a[i].x[0] = (LL)(x + y) * 2;
a[i].x[1] = (LL)(x - y) * 2;
}
sort(a, a + n);
LL l = 0, r = infLL;
while (l < r) {
LL mid = l + ((r - l) >> 1);
if (check(mid)) r = mid;
else l = mid + 1;
}
printf("Case #%d:\n", ++cas);
printf("%I64d/%I64d\n", r & 1? r: r / 2, (r & 1) + 1);
}
return 0;
}
1002 连接的管道
题目大意
有一个片N*M大小的农田,每块田有个高度,两块相邻的田之间修管道的代价为高度差
现在要用管道把所有农田连接起来,问最小代价
算法思路
网格图版的最小生成树
时间复杂度: O(N2logN2)
代码
/**
* Copyright © 2015 Authors. All rights reserved.
*
* FileName: B.cpp
* Author: Beiyu Li <sysulby@gmail.com>
* Date: 2015-06-09
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <queue>
using namespace std;
#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
typedef long long LL;
typedef pair<int, int> Pii;
const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;
const int maxn = 1000 + 5;
int n, m;
int a[maxn][maxn];
int dis[maxn][maxn];
bool vis[maxn][maxn];
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
bool inside(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }
int prim()
{
int res = 0;
priority_queue<Pii> que;
memset(dis, 0x3f, sizeof(dis));
memset(vis, false, sizeof(vis));
dis[0][0] = 0; que.push(Pii(0, 0));
while (!que.empty()) {
int x = que.top().second / m, y = que.top().second % m;
que.pop();
if (vis[x][y]) continue; vis[x][y] = true, res += dis[x][y];
rep(i,4) {
int nx = x + dx[i], ny = y + dy[i];
if (!inside(nx, ny)) continue;
int w = abs(a[x][y] - a[nx][ny]);
if (w < dis[nx][ny]) {
dis[nx][ny] = w;
que.push(Pii(-dis[nx][ny], nx * m + ny));
}
}
}
return res;
}
int main()
{
int T, cas = 0;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
rep(i,n) rep(j,m) scanf("%d", &a[i][j]);
printf("Case #%d:\n", ++cas);
printf("%d\n", prim());
}
return 0;
}
1003 棋盘占领
题目大意
有一个N*M的棋盘,一开始占领了一些格子
如果一个格子上下左右相邻的4个格子中,存在两个有公共点的格子被占领,则该格子也被占领
问最后有多少个格子被占领
算法思路
一旦一个格子被占领,最多改变其上下左右相邻的4个格子的状态
使用BFS,每次考察与队头相邻的格子是否即将被占领,入队即可
时间复杂度: O(N2)
代码
/**
* Copyright © 2015 Authors. All rights reserved.
*
* FileName: C.cpp
* Author: Beiyu Li <sysulby@gmail.com>
* Date: 2015-06-09
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
typedef long long LL;
typedef pair<int, int> Pii;
const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;
const int maxn = 500 + 5;
int n, m, g;
bool vis[maxn][maxn];
int dx[] = {-1, 0, 1, 0}, dy[] = {0, -1, 0, 1};
bool inside(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }
bool check(int x, int y)
{
rep(i,4) {
int x1 = x + dx[i], y1 = y + dy[i];
int x2 = x + dx[(i+1)%4], y2 = y + dy[(i+1)%4];
if (inside(x1, y1) && vis[x1][y1] &&
inside(x2, y2) && vis[x2][y2]) return true;
}
return false;
}
int main()
{
int T, cas = 0;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m, &g);
int res = 0, x, y;
queue<Pii> que;
memset(vis, false, sizeof(vis));
while (g--) {
scanf("%d%d", &x, &y);
que.push(Pii(--x, --y));
}
while (!que.empty()) {
int x = que.front().first, y = que.front().second;
que.pop();
if (vis[x][y]) continue; vis[x][y] = true, ++res;
rep(i,4) {
int nx = x + dx[i], ny = y + dy[i];
if (!inside(nx, ny)) continue;
if (check(nx, ny)) que.push(Pii(nx, ny));
}
}
printf("Case #%d:\n", ++cas);
printf("%d\n", res);
}
return 0;
}
1004 魔法因子
题目大意
对于给出的实数X,找出所有的不超过10位的正整数N
满足N*X依然是整数,且刚好是N首位和末位交换位置的形式
算法思路
令
N=a∗10k+b∗10+c
,有
N∗X=c∗10k+b∗10+a
可得
b=X∗(a∗10k+c)−(c∗10k+a)1−X÷10
把
X
写成分数形式
由此,我们可以枚举a、c和k的值,确定能否得到满足范围的b
时间复杂度: O(103)
代码
/**
* Copyright © 2015 Authors. All rights reserved.
*
* FileName: D.cpp
* Author: Beiyu Li <sysulby@gmail.com>
* Date: 2015-06-10
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
typedef long long LL;
typedef pair<int, int> Pii;
const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;
int main()
{
int T, cas = 0;
scanf("%d", &T);
while (T--) {
double x;
scanf("%lf", &x);
int y = x * 1000000 + 0.5, z = 1000000, m = (z - y) * 10;
vector<LL> vec;
for (LL i = 1, p = 10; i < 10; ++i, p *= 10) {
for (int a = 1; a < 10; ++a) rep(c,10) {
LL b = y * (a * p + c) - z * (c * p + a);
if (b % m) continue; b /= m;
if (0 <= b && b * 10 < p)
vec.push_back(a * p + b * 10 + c);
}
}
sort(vec.begin(), vec.end());
printf("Case #%d:\n", ++cas);
printf("%d\n", (int)vec.size());
foreach(it,vec) printf("%I64d%c", *it, " \n"[it==--vec.end()]);
}
return 0;
}
1005 序列变换
题目大意
给出一个整数数列,要求调整最少的数,使得该数列严格递增
算法思路
对于任意两个无需调整的数,必须有
Aj−Aj≥j−i
,即
Ai−i≤Aj−j
因此,将数列每一位的值减去下标,则无需调整的数必定构成非降子序列
找出最长的非降子序列长度,用总长度减去就是最少需要调整的个数
时间复杂度: O(NlogN)
代码
/**
* Copyright © 2015 Authors. All rights reserved.
*
* FileName: E.cpp
* Author: Beiyu Li <sysulby@gmail.com>
* Date: 2015-06-10
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
using namespace std;
#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
typedef long long LL;
typedef pair<int, int> Pii;
const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;
const int maxn = 100000 + 5;
int n;
int a[maxn], g[maxn];
int main()
{
int T, cas = 0;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
rep(i,n) scanf("%d", &a[i]);
memset(g, 0x3f, sizeof(g));
int ret = 0;
rep(i,n) {
int p = upper_bound(g + 1, g + n + 1, a[i] - i) - g;
ret = max(ret, p);
g[p] = a[i] - i;
}
printf("Case #%d:\n", ++cas);
printf("%d\n", n - ret);
}
return 0;
}
1006 翻转游戏
题目大意
在一个N*M的格子上,有一些格子是黑色,有一些是白色
每选择一个格子按一次,格子以及周围边相邻的格子都会翻转颜色
有一些格子坏掉了,无法被按下,问能否把所有格子都变成白色
算法思路
首先,每个格子最多操作一次,因此每个格子操作与否可以用0/1状态表示
其次,由于操作的效果是置反,所以每个格子的状态改变,相当于相关操作的异或
最后,由于每次操作最多影响到上面一行,所以当第一行的操作确定后
第一行的状态只能通过第二行的操作来调整,因此第二行的操作也随之确定
由此,每个格子的操作都可以表示为第一行操作的线性异或组合
这样,我们把第一行的操作设为M个0/1变量,每个格子的操作都是一个异或方程
对于K个坏掉的格子,以及第N+1行的格子(假设存在),操作的结果必须是0
答案等价于这些方程组是否有解,可以用高斯消元求解
时间复杂度: O(N3)
代码
/**
* Copyright © 2015 Authors. All rights reserved.
*
* FileName: F.cpp
* Author: Beiyu Li <sysulby@gmail.com>
* Date: 2015-06-10
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <bitset>
using namespace std;
#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
typedef long long LL;
typedef pair<int, int> Pii;
const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;
const int maxn = 256 + 5;
int gauss(bitset<maxn> a[], int n, int m)
{
int rank = 0;
for (int i = 0; i < m; ++i) {
int r = rank;
while (r < n && !a[r].test(i)) ++r;
if (r >= n) continue;
swap(a[rank], a[r]);
for (int j = rank + 1; j < n; ++j)
if (a[j].test(i)) a[j] ^= a[rank];
++rank;
}
for (int i = rank; i < n; ++i) if (a[i].test(m)) return -1;
return rank;
}
int n, m, k;
bool g[maxn][maxn], a[maxn][maxn];
bitset<maxn> b[maxn][maxn], vec[maxn*2];
int main()
{
int T, cas = 0;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m, &k);
rep(i,n) {
char s[maxn];
scanf("%s", s);
rep(j,m) g[i][j] = (s[j] == 'B');
}
memset(a, true, sizeof(a));
rep(i,k) {
int x, y;
scanf("%d%d", &x, &y);
a[--x][--y] = false;
}
k = 0;
rep(j,m) b[0][j].reset(), b[0][j].set(j);
rep(i,n) {
rep(j,m) if (!a[i][j]) vec[k++] = b[i][j];
rep(j,m) {
b[i+1][j] = b[i][j];
if (i) b[i+1][j] ^= b[i-1][j];
if (j) b[i+1][j] ^= b[i][j-1];
if (j + 1 < m) b[i+1][j] ^= b[i][j+1];
if (g[i][j]) b[i+1][j].flip(m);
}
}
rep(j,m) vec[k++] = b[n][j];
printf("Case #%d:\n", ++cas);
puts(~gauss(vec, k, m)? "YES": "NO");
}
return 0;
}