您的位置:首页 > 其它

AtCoder Regular Contest 089 D - Checker

2018-01-27 17:02 585 查看

题目大意

现有一个二维平面,并定义一个名词“ KK 单位块” ;“ K<
4000
/span>K 单位块” 表示以 K×KK×K 的方格为单位,将二维平面涂成黑白相间的形式。如图就是一个“33 单位块” 涂成的二维平面:



现有 NN 个点,以及各点的期望颜色(‘B′‘B′ 表示黑色,′W′′W′ 表示白色)。问最多有几个点能被涂成所期望的颜色?

题目链接

ARC089-D-Checker

数据范围

1≤N≤1051≤K≤10001≤N≤1051≤K≤1000

0≤xi,yi≤1090≤xi,yi≤109 且 i≠ji≠j 时 (xi,yi)≠(xj,yj)(xi,yi)≠(xj,yj)

解题思路

如果一个点 (x,y)(x,y) 的期望颜色是白色,相当于点 (x,y+K)(x,y+K) 的期望颜色是黑色 (其实(x+K,y)(x+K,y) 也是黑色,这里只为了说明思路);那么 (x,y,′W′)(x,y,′W′) 就可以转化为 (x,y+k,′B′)(x,y+k,′B′) 。

如果一个点 (x,y)(x,y) 的期望色是黑色,点 (x+2K,y+2K)(x+2K,y+2K) 的颜色是一样的,那么 (x,y,′B′)(x,y,′B′) 和 (x%2K,y%2K,′B′)(x%2K,y%2K,′B′) 是等价的。

通过这两步转化之后,就可以在 2K×2K2K×2K 的平面中求解了。如果通过枚举左下角、依次 check ,复杂度是不够的。用 矩阵前缀和 优化一下,O(1)O(1) 回答某个矩阵和,复杂度就降为了 O(K2+N)O(K2+N) 。

太菜了!

因为涂刷方案有很多种,所以开始还在想怎么计算那些“单位块零散”的方案;后来看了大S们的代码,搞个 4K×4K4K×4K 的数组不就可以了!就像以前一圈儿数做开头,数组开 22 倍一样。

mod 完之后坐标范围为 [0,2K)[0,2K) ,在查询坐标含0的矩阵的,因处理不当RE了一发,改完之后代码已经100+了;后来看大S们的代码,哈!居然用后缀和这种骚操作,改完之后代码80+ 。(贴出后缀和代码)

AC代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const LL INF = 1LL << 60;
const int MaxN = 100005;
const int MaxM = 1000;

int n, k;
int M;
struct Point {
int x, y;
char c;
Point () {}
Point (int _x, int _y, char _c) {
x = _x; y = _y; c = _c;
}
}PP[MaxN + 5];
int sum[4 * MaxM + 5][4 * MaxM + 5];

void init() {
M = 2 * k;
memset(sum, 0, sizeof(sum));
for(int i = 1; i <= n; i++)
scanf("%d %d %c", &PP[i].x, &PP[i].y, &PP[i].c);
}

int cal(int lx, int ly, int ux, int uy) {   //计算某个矩阵和
return sum[lx][ly] - sum[lx][uy + 1] - sum[ux + 1][ly]
+ sum[ux + 1][uy + 1];
}

void change() {
for(int i = 1; i <= n; i++) {    //转化
if(PP[i].c == 'B') {
int tx = PP[i].x % M;
int ty = PP[i].y % M;
sum[tx][ty]++;
}
else if(PP[i].c == 'W') {
int tx = (PP[i].x) % M;
int ty = (PP[i].y + k) % M;
sum[tx][ty]++;
}
}
for(int i = 0; i < M; i++) {    //扩充矩阵
for(int j = 0; j < M; j++) {
sum[i + M][j] = sum[i][j];
sum[i][j + M] = sum[i][j];
sum[i + M][j + M] = sum[i][j];
}
}
for(int i = 2 * M - 1; i >= 0; i--) {    //计算后缀和
for(int j = 2 * M - 1; j >= 0; j--) {
sum[i][j] = sum[i][j] + sum[i + 1][j] +
sum[i][j + 1] - sum[i + 1][j + 1];
}
}
}

void solve() {
int best = 0;
for(int i = 0; i < M; i++) {
for(int j = 0; j < M; j++) {
int tmp = cal(i, j, i + k - 1, j + k - 1)
+ cal(i + k, j + k, i + M - 1, j + M - 1);
best = max(best, tmp);
}
}
printf("%d\n", best);
}

int main()
{
while(scanf("%d %d", &n, &k) != EOF)
{
init();
change();
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: