AtCoder Regular Contest 089 D - Checker

题目大意

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

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

题目链接

ARC089-D-Checker

数据范围

1N1051K1000 1 ≤ N ≤ 10 5 1 ≤ K ≤ 1000

0xi,yi109 0 ≤ x i , y i ≤ 10 9 ij 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 <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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值