2023NOIP A层联测23-涂鸦

有一面由 n × m n\times m n×m 个格子组成的墙,每个格子要么是黑色,要么是白色。你每次将会进行这样的操作:等概率随机选择一个位置 ( x , y ) (x,y) (x,y),和一个颜色 c c c(黑色或者白色)( 1 ≤ x ≤ n , 1 ≤ y ≤ m 1≤x≤n,1≤y≤m 1xn,1ym,任意 ( x , y , c ) (x,y,c) (x,y,c) 的组合选择它的概率均为 1 2 ∗ n ∗ m \frac1{2∗n∗m} 2nm1),然后将在 ( x , y ) (x,y) (x,y) 左上⻆的所有格子的颜色涂成 c c c。即将所有满足 1 ≤ x ′ ≤ x , 1 ≤ y ′ ≤ y 1≤x′≤x,1≤y′≤y 1xx,1yy ( x ′ , y ′ ) (x′,y′) (x,y) 格子上的颜色涂成 c c c。这次操作的代价为涂的格子的数量,即 x × y x\times y x×y。给定初始状态和终止状态,问期望要花费多少代价才能将墙面从初始状态涂成终止状态。答案模 998244353 998244353 998244353

n , m ≤ 5 n,m\le5 n,m5


先考虑朴素的 dp,对墙上的每个格子进行状态压缩,总共有 2 n m 2^{nm} 2nm 个状态,设 f i f_i fi 表示状态 i i i 期望还要花费多少代价才能到达最终状态。转移为
f i = ∑ f j + w 2 n m f_i=\sum\dfrac{f_j+w}{2nm} fi=2nmfj+w

其中 j j j 表示一个格子的左上角都涂成黑色或白色所得到的状态, w w w 是涂一次颜色的代价。

这是有后效性 dp,可以高斯消元解决,时间复杂度 O ( 2 3 n m ) O(2^{3nm}) O(23nm),可以得到 60pts。

下面考虑减少状态。

考虑一个状态,若一个格子的颜色与最终状态的颜色不同,则这个点后面一定会修改,其左上角的点同样,所以这些点的颜色是什么就不重要了,反正后面都要被改。记 p i , j p_{i,j} pi,j 表示状态中坐标为 ( i , j ) (i,j) (i,j) 的点的颜色是否(1/0)与终止状态一样,通过模拟可以发现, p p p 数组中构成 1 1 1 的元素是类似阶梯的形状。如下图,圆点表示在这个格子颜色与最终状态的不一样,矩形表示
范围内的格子要被修改,红色部分就是 p i , j p_{i,j} pi,j 1 1 1 的部分。

2023NOIP A层联测23-涂鸦_第1张图片
称这个矩阵为阶梯矩阵,原来的状态称为 01 矩阵。

那么,所有的 01 矩阵都能唯一转化为一个阶梯矩阵,所以只需将原来的状态换成新的阶梯状态进行高斯消元即可。

而阶梯状态是不多的,只有 ( n + m n ) \binom{n+m}{n} (nn+m) 种,下面证明。

考虑每一行,看每行红色部分的格子数量,容易发现它们是有单调性的,设格子数量为 i i i 的行数为 x i x_i xi,即方案就是求 ∑ i = 0 n x i = m \sum\limits_{i=0}^nx_i=m i=0nxi=m 的解的个数,这是组合数学的经典问题,易证。

( n + m n ) \binom{n+m}{n} (nn+m) 最大也就 ( 10 5 ) = 252 \binom{10}{5}=252 (510)=252,高斯消元绰绰有余。

实现上,可以用 dfs 求出阶梯矩阵的状态和个数,01 矩阵转换成阶梯矩阵直接暴力枚举(时间充裕)每个点,若颜色不相同就直接对左上角修改。高斯消元要写模意义下的,我的实现直接用快速幂求逆元。

总的时间复杂度为 O ( ( n + m n ) 3 + ( n + m n ) n 3 m 3 ) O\left(\binom{n+m}{n}^3+\binom{n+m}{n}n^3m^3\right) O((nn+m)3+(nn+m)n3m3)

具体实现参照代码

#include
using namespace std;
#define ll long long
const ll mod=998244353;
const int N=260;
ll a[N][N],val[N];
int fr[N],nn,mm,n,m,cnt;
char s1[N][N],s2[N][N];
unordered_map<int,int> ma;
vector<int> v;
ll ksm(ll a,ll b)
{
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}
int gauss()
{
    int r=1,c=1;
    for(;r<=nn&&c<=mm;r++,c++){
        int maxn=r;
        for(int i=r+1;i<=nn;i++) if(abs(a[maxn][c])<abs(a[i][c])) maxn=i;
        for(int j=1;j<=mm+1;j++){
            swap(a[maxn][j],a[r][j]);
        }
        if(abs(a[r][c])==0){r--;continue;}
        for(int i=1;i<=nn;i++){
            if(i==r) continue;
            ll g=(a[i][c]*ksm((a[r][c]%mod+mod)%mod,mod-2)%mod+mod)%mod;
            for(int j=1;j<=mm+1;j++)
                a[i][j]=(a[i][j]-a[r][j]*g%mod+2*mod)%mod;
        }
    }
    for(int i=r;i<=nn;i++){
        if(abs(a[i][i])==0&&abs(a[i][mm+1])>0){
            return -1;
        }
    }
    memset(fr,0x3f,sizeof(fr));
    for(int i=1;i<r;i++){
        int cnt=0,num=0;
        for(int j=1;j<=mm;j++) if(fr[j]&&abs(a[i][j])>0) cnt++,num=j;
        if(cnt==1) fr[num]=0,val[num]=(a[i][mm+1]*ksm((a[i][num]%mod+mod)%mod,mod-2)%mod+mod)%mod;
    }
    return 1;
}
int getid(int x,int y){return (x-1)*m+y;}
void dfs(int x,int k,int state)
{
    if(!x){
        ma[state]=++cnt;
        v.push_back(state);
        return;
    }
    for(int i=0;i<=k;i++) dfs(x-1,i,state|((1<<x*m)-1^(1<<x*m-i)-1));
}
int change(int id)
{
    int newid=(1<<n*m)-1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if((id>>getid(i,j)-1&1)!=(s2[i][j]=='B')){
                for(int ii=1;ii<=i;ii++){
                    for(int jj=1;jj<=j;jj++){
                        newid&=INT_MAX^(1<<getid(ii,jj)-1);
                    }
                }
            }
        }
    }
    return newid;
}
int main()
{
    freopen("graffiti.in","r",stdin);
    freopen("graffiti.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%s",s1[i]+1);
    for(int i=1;i<=n;i++) scanf("%s",s2[i]+1);
    int finish=0,start=0;
    for(int i=n;i>=1;i--){
        for(int j=m;j>=1;j--){
            finish=finish*2+(s2[i][j]=='B');
            start=start*2+(s1[i][j]=='B');
        }
    }
    dfs(n,m,0);
    int N=1<<n*m;
    nn=mm=v.size();
    for(int i=0;i<v.size();i++){
        int t=v[i];
        if(i==ma[change(finish)]-1){
            a[i+1][i+1]=1;
            continue;
        }
        int tt=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(t>>getid(i,j)-1&1) tt|=(s2[i][j]=='B')<<getid(i,j)-1;
                else tt|=(s2[i][j]=='W')<<getid(i,j)-1;
            }
        }
        a[ma[t]][ma[t]]+=2*n*m;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                int id=tt;
                for(int ii=1;ii<=i;ii++){
                    for(int jj=1;jj<=j;jj++){
                        id|=1<<(getid(ii,jj)-1);
                    }
                }
                int newid=change(id);
                a[ma[t]][nn+1]+=i*j;
                a[ma[t]][ma[newid]]--;
                for(int ii=1;ii<=i;ii++){
                    for(int jj=1;jj<=j;jj++){
                        id^=1<<(getid(ii,jj)-1);
                    }
                }
                newid=change(id);
                a[ma[t]][nn+1]+=i*j;
                a[ma[t]][ma[newid]]--;
            }
        }
    }
    int czn=gauss();
    printf("%lld\n",val[ma[change(start)]]);
}

你可能感兴趣的:(算法)