现有一个二维平面,并定义一个名词“ K K 单位块” ;“ K K 单位块” 表示以 K×K K × K 的方格为单位,将二维平面涂成黑白相间的形式。如图就是一个“ 3 3 单位块” 涂成的二维平面:
现有 N N 个点,以及各点的期望颜色( ‘B′ ‘ B ′ 表示黑色, ′W′ ′ W ′ 表示白色)。问最多有几个点能被涂成所期望的颜色?
ARC089-D-Checker
1≤N≤1051≤K≤1000 1 ≤ N ≤ 10 5 1 ≤ K ≤ 1000
0≤xi,yi≤109 0 ≤ x i , y i ≤ 10 9 且 i≠j i ≠ j 时 (xi,yi)≠(xj,yj) ( x i , y i ) ≠ ( x j , y j )
如果一个点 (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 + 2 K , y + 2 K ) 的颜色是一样的,那么 (x,y,′B′) ( x , y , ′ B ′ ) 和 (x%2K,y%2K,′B′) ( x % 2 K , y % 2 K , ′ B ′ ) 是等价的。
通过这两步转化之后,就可以在 2K×2K 2 K × 2 K 的平面中求解了。如果通过枚举左下角、依次 check ,复杂度是不够的。用 矩阵前缀和 优化一下, O(1) O ( 1 ) 回答某个矩阵和,复杂度就降为了 O(K2+N) O ( K 2 + N ) 。
太菜了!
因为涂刷方案有很多种,所以开始还在想怎么计算那些“单位块零散”的方案;后来看了大S们的代码,搞个 4K×4K 4 K × 4 K 的数组不就可以了!就像以前一圈儿数做开头,数组开 2 2 倍一样。
mod 完之后坐标范围为 [0,2K) [ 0 , 2 K ) ,在查询坐标含0的矩阵的,因处理不当RE了一发,改完之后代码已经100+了;后来看大S们的代码,哈!居然用后缀和这种骚操作,改完之后代码80+ 。(贴出后缀和代码)
AC代码:
#include
#include
#include
#include
#include
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;
}