「51Nod 1824」染色游戏

Description

很奇妙,FMT 有趣题。

首先可以一眼(或两眼)看出(或推出)式子:

f(t)x=0t(tx)rxbtxmod2 f ( t ) ≡ ∑ x = 0 t ( t x ) r x b t − x mod 2

这好像是个卷积,FFT???然后就想歪了。

根据 Lucas 定理,我们有

(tx)(a0b0)...(arbr)mod2 ( t x ) ≡ ( a 0 b 0 ) . . . ( a r b r ) mod 2
a0+...+2rar=t,ai{0,1} a 0 + . . . + 2 r a r = t , a i ∈ { 0 , 1 }
b0+...+2rbr=x,bi{0,1} b 0 + . . . + 2 r b r = x , b i ∈ { 0 , 1 }

我们发现当且仅当

(aibi)=[aibi] ( a i b i ) = [ a i ≤ b i ]

深思一番后,发现这相当于 t t 二进制位构成的集合属于 x 二进制位构成的集合,即 tx t ⊆ x 假装 t,x 是集合

式子等价于:

f(t)txrxbtxmod2 f ( t ) ≡ ∑ t ⊆ x r x b t − x mod 2

这就是一个 FMT 了,如果直接上的话, O(log2n2logn) O ( log n 2 ⁡ 2 log n )
因为只需要知道奇偶性,所以加个 bitset 优化,则可以做到 O(logn2logn) O ( log n ⁡ 2 log n )

#include <cstdio>
#include <cstring>
#include <bitset>
int n, m;
char r[1 << 20 | 15], b[1 << 20 | 15];
int f[1 << 21], R[1 << 21], B[1 << 21];
std::bitset<1 << 21> ft[21], Rt[21], Bt[21];
int main()
{
    scanf("%d %d", &n, &m);
    scanf("%s %s", r + 1, b + 1);
    for(int i = 1; i <= n; ++i) (r[i] -= '0') &= 1; r[0] = 1;
    for(int i = 1; i <= m; ++i) (b[i] -= '0') &= 1; b[0] = 1;
    for(int i = 0; i <= n; ++i) if(r[i])
    {
        int cnt = 0;
        for(int j = 0; j < 21; ++j) cnt += i >> j & 1;
        R[i] = 1 << cnt;
    }
    for(int i = 0; i <= m; ++i) if(b[i])
    {
        int cnt = 0;
        for(int j = 0; j < 21; ++j) cnt += i >> j & 1;
        B[i] = 1 << cnt;
    }
    for(int i = 0; i < 21; ++i)
        for(int j = 0; j < (1 << 21); ++j) if(!(j >> i & 1))
        {
            R[j | 1 << i] ^= R[j];
            B[j | 1 << i] ^= B[j];
        }
    for(int i = 0; i < (1 << 21); ++i)
        for(int j = 0; j < 21; ++j)
        {
            if(R[i] >> j & 1) Rt[j][i] = 1;
            if(B[i] >> j & 1) Bt[j][i] = 1;
        }
    for(int k = 0; k < 21; ++k)
        for(int d = 0; d <= k; ++d)
            ft[k] = ft[k] ^ (Rt[d] & Bt[k - d]);
    for(int i = 0; i < 21; ++i)
        for(int j = 0; j < (1 << 21); ++j) if(ft[i][j])
            f[j] |= 1 << i;
    for(int i = 0; i < 21; ++i)
        for(int j = 0; j < (1 << 21); ++j) if(!(j >> i & 1))
            f[j | 1 << i] ^= f[j];
    long long Ans = 0;
    for(int i = 1; i <= n + m; ++i)
    {
        int cnt = 0;
        for(int j = 0; j < 21; ++j) cnt += i >> j & 1;
        if(f[i] >> cnt & 1) Ans += 1ll * i * i;
    }
    printf("%lld\n", Ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值