cf1270e 题解

发现题解取都是四染色, 发一个二染色的方法.

考虑对格子黑白染色, 坐标和为偶数的染为黑色. 这里我们先假设黑白色都有.

如果所有点分为黑白两组, 满足组间的边为黄色, 组内的边为蓝色, 那么黄色的边平方一定是奇数, 蓝色边的平方一定是偶数, 满足题目条件.

那么我们直接输出即可.

如果只存在一种颜色的点, 假设只存在黑点, 如果只有白点可以全部下移一格转为全为黑点.

现在我们把所有点的坐标由 ( x , y ) (x,y) (x,y) 变为 ( x + y ​ 2 , x − y 2 ​ ) (\frac{x+y​}2,\frac{x−y}2​) (2x+y,2xy) , 即进行旋转, 因为全都是黑点那么不会出现小数, 那么如果这时候分为了黑白点就可以由之前的方法解决, 如果还是只有一种颜色就继续递归旋转

现在证明递归的次数为 O ( l o g A ) O(logA) O(logA)次, 其中 A 为值域大小.

我们发现旋转一次一个点到原点的距离的平方除以了二, 但是一个点永远不会变成原点, 且一直是整点, 所以最多会旋转 O ( l o g A ) O(logA) O(logA) 次.

代码

#include <bits/stdc++.h>
#define il inline
#define _for(i, a, b) for (int i = (a); i <= (b); ++i)
#define _fore(i, u) for (int i = hd[u], v; v = tg[i], i; i = nx[i])
using namespace std;
struct {
    template <typename T>
    il operator T() {
        T x = 0, f = 1;
        char c = getchar();
        while (!isdigit(c)) (c == '-') && (f = -1), c = getchar();
        while (isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f * x;
    }
} gi;
const int N = 1e3 + 5;
int n, x[N], y[N];
int s[N], ts;
int main() {
    n = gi;
    for (int i = 1; i <= n; ++i) x[i] = gi, y[i] = gi;
    while (1) {
        int _a0 = 1, _a1 = 1;
        for (int i = 1; i <= n; ++i) _a0 &= !((x[i] + y[i]) & 1), _a1 &= (x[i] + y[i]) & 1;
        if (!_a0 && !_a1) {
            for (int i = 1; i <= n; ++i)
                if ((x[i] + y[i]) & 1) s[++ts] = i;
            break;
        }
        if (_a1)
            for (int i = 1; i <= n; ++i) x[i]--;
        for (int i = 1, x0, y0; i <= n; ++i) x0 = x[i], y0 = y[i], x[i] = (x0 + y0) >> 1, y[i] = (x0 - y0) >> 1;
    }
    printf("%d\n", ts);
    for (int i = 1; i <= ts; ++i) printf("%d ", s[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值