8.4()

CF1574E

题意:

  • 给一个 \(n \times m\) 的 \(空/0/1\) 矩阵(初始全空)。
  • \(k\) 次操作,每次操作会把一个格子样式修改。
  • 求每次修改后填充剩下空格子的方案数,使所有 \(2 \times 2\) 的子矩阵 \(4\) 个元素之和为 \(2\) (也就是有 \(2\) 个 \(1\) 和 \(2\) 个 \(0\))。
  • \(n,m \leq 10^6,k \leq 3 \times 10^5\),答案对 \(998244353\) 取模。

题解:

考虑所有满足的要求的\(2*2\)的格子:

要么它的两行上的数字相反,要么两列上的数字相反。或者行和列上的数字都相反(\(01\)间隔)

拓展一下,\(n*m\)的满足要求的矩阵,要么每两行之间的数字都是相反,要么每两列的数字都是相反的,或者两者都满足。

那么其实整个网格的填数方案都取决于第一行和第一列的填数方案。

以第一行举例子,当一个位置上的数字确定时,它所对应的行首填的数就确定了。

如果在行上没有冲突,那么方案就是\(2^x\),其中\(x\)是空闲行的数量。

列上同理,不过要容斥掉行和列都满足的情况,也就是所有格子都\(01\)相间的情况。

这种情况最多有两种。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=2e6+10,mod=998244353,inv2=5e8+4,inf=2e9;
    int fast(int x,int k)
    {
        int ret=1;
        while(k)
        {
            if(k&1) ret=ret*x%mod;
            x=x*x%mod;
            k>>=1;
        }
        return ret;
    }
    inline void main()
    {
        int n,m,k;cin>>n>>m>>k;
        int sum1=n,sum2=m;
        vector a(n+1,vector<int>(2));
        vector b(m+1,vector<int>(2));
        vector<int> d(2);
        int c1=0,c2=0;
        vector<map<int,int>> mp(1000001);
        auto del=[&](int x,int y) -> void
        {
            if(!mp[x][y]) return;
            int c=mp[x][y]-1;mp[x][y]=0;
            if(a[x][(y&1)^c]==1&&a[x][(y&1)^c^1]>0) --c1;
            if(b[y][(x&1)^c]==1&&b[y][(x&1)^c^1]>0) --c2;
            if(a[x][(y&1)^c]==1) ++sum1;
            if(b[y][(x&1)^c]==1) ++sum2;
            --a[x][(y&1)^c];
            --b[y][(x&1)^c];
            --d[((x+y)&1)^c];
        };
        auto add=[&](int x,int y,int c) -> void
        {
            mp[x][y]=c+1;
            if(a[x][(y&1)^c]==0&&a[x][(y&1)^c^1]>0) ++c1;
            if(b[y][(x&1)^c]==0&&b[y][(x&1)^c^1]>0) ++c2;
            if(a[x][(y&1)^c]==0) --sum1;
            if(b[y][(x&1)^c]==0) --sum2;
            ++a[x][(y&1)^c];
            ++b[y][(x&1)^c];
            ++d[((x+y)&1)^c];
        };
        auto query=[&](int x,int y) -> int
        {
            int sum=0;
            if(!c1) sum=(sum+fast(2,sum1));
            if(!c2) sum=(sum+fast(2,sum2));
            if(!d[0]) --sum;
            if(!d[1]) --sum;
            return (sum%mod+mod)%mod;
        };
        for(int i=1;i<=k;++i)
        {
            int x,y,z;cin>>x>>y>>z;
            del(x,y);
            if(z!=-1) add(x,y,z);
            cout<<query(x,y)<<'\n';
        }
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    red::main();
    return 0;
}
/*

*/
————————

CF1574E

题意:

  • 给一个 \(n \times m\) 的 \(空/0/1\) 矩阵(初始全空)。
  • \(k\) 次操作,每次操作会把一个格子样式修改。
  • 求每次修改后填充剩下空格子的方案数,使所有 \(2 \times 2\) 的子矩阵 \(4\) 个元素之和为 \(2\) (也就是有 \(2\) 个 \(1\) 和 \(2\) 个 \(0\))。
  • \(n,m \leq 10^6,k \leq 3 \times 10^5\),答案对 \(998244353\) 取模。

题解:

考虑所有满足的要求的\(2*2\)的格子:

要么它的两行上的数字相反,要么两列上的数字相反。或者行和列上的数字都相反(\(01\)间隔)

拓展一下,\(n*m\)的满足要求的矩阵,要么每两行之间的数字都是相反,要么每两列的数字都是相反的,或者两者都满足。

那么其实整个网格的填数方案都取决于第一行和第一列的填数方案。

以第一行举例子,当一个位置上的数字确定时,它所对应的行首填的数就确定了。

如果在行上没有冲突,那么方案就是\(2^x\),其中\(x\)是空闲行的数量。

列上同理,不过要容斥掉行和列都满足的情况,也就是所有格子都\(01\)相间的情况。

这种情况最多有两种。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=2e6+10,mod=998244353,inv2=5e8+4,inf=2e9;
    int fast(int x,int k)
    {
        int ret=1;
        while(k)
        {
            if(k&1) ret=ret*x%mod;
            x=x*x%mod;
            k>>=1;
        }
        return ret;
    }
    inline void main()
    {
        int n,m,k;cin>>n>>m>>k;
        int sum1=n,sum2=m;
        vector a(n+1,vector<int>(2));
        vector b(m+1,vector<int>(2));
        vector<int> d(2);
        int c1=0,c2=0;
        vector<map<int,int>> mp(1000001);
        auto del=[&](int x,int y) -> void
        {
            if(!mp[x][y]) return;
            int c=mp[x][y]-1;mp[x][y]=0;
            if(a[x][(y&1)^c]==1&&a[x][(y&1)^c^1]>0) --c1;
            if(b[y][(x&1)^c]==1&&b[y][(x&1)^c^1]>0) --c2;
            if(a[x][(y&1)^c]==1) ++sum1;
            if(b[y][(x&1)^c]==1) ++sum2;
            --a[x][(y&1)^c];
            --b[y][(x&1)^c];
            --d[((x+y)&1)^c];
        };
        auto add=[&](int x,int y,int c) -> void
        {
            mp[x][y]=c+1;
            if(a[x][(y&1)^c]==0&&a[x][(y&1)^c^1]>0) ++c1;
            if(b[y][(x&1)^c]==0&&b[y][(x&1)^c^1]>0) ++c2;
            if(a[x][(y&1)^c]==0) --sum1;
            if(b[y][(x&1)^c]==0) --sum2;
            ++a[x][(y&1)^c];
            ++b[y][(x&1)^c];
            ++d[((x+y)&1)^c];
        };
        auto query=[&](int x,int y) -> int
        {
            int sum=0;
            if(!c1) sum=(sum+fast(2,sum1));
            if(!c2) sum=(sum+fast(2,sum2));
            if(!d[0]) --sum;
            if(!d[1]) --sum;
            return (sum%mod+mod)%mod;
        };
        for(int i=1;i<=k;++i)
        {
            int x,y,z;cin>>x>>y>>z;
            del(x,y);
            if(z!=-1) add(x,y,z);
            cout<<query(x,y)<<'\n';
        }
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    red::main();
    return 0;
}
/*

*/