[JSOI2018]扫地机器人

题目大意

[JSOI2018]扫地机器人_第1张图片
[JSOI2018]扫地机器人_第2张图片
T为数据组数

解题思路

实际上是一道打表题(逃
手玩会发现有很多不合法的情况,而且看起来合法的方案,(i,j)和(i+1,j-1)是一样的。
我们不妨把方案打出来看看。先考虑n=m。
会发现确定了第一行之后,以后每一行都是上一行往前轮换一个,也就是第一个移动到最后,第i个到i-1的位置。
n!=m怎么办呢?设d=gcd(n,m),会发现如果你决策了d*d这个矩阵,然后把它copy n*m/d次,他大概率是一个可行的方案。然后再打表能发现,只有当d*d这个矩阵第一行向下的个数(设为i)和n互质的时候,才是一个合法解,当然第一列的向右的个数也要满足和m互质;(你横着看矩阵,n和m交换,向右也变成了向下)。
然后发现这些就是合法的充要条件了。
考虑回来,那么除了第一个都是障碍的情况,我们可以枚举合法的i然后组合数算方案数就行了。
否则怎么做呢?
考虑到每d步走出来的路径是完全一样的,而且确定了i之后,走d步x和y坐标的增量是固定的,那么每次可以拉出一个x*y的矩阵。那么枚举它在nm/d轮的哪一轮碰到障碍,计算出之前的轮里都碰不到障碍的方案数之类的东西,贡献给答案就可以了,具体地说,每次要把前面几轮的障碍投射到当前的矩阵里。大概就是一个路径dp,设f[i][j]表示走到第i行第j列之类的。
复杂度大概O(n^3)。

代码

#include 
#include
#include
#include
#include
//开 O2!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a
typedef long long ll;
typedef double db;
const int N=200+5,mo=998244353;
int a[N][N],c[N][N],n,m,i,j,d,ans,T,TT,f[N][N],b[N][N],g[N][N];
char s[N];
int gcd(int a,int b)
{
    if (!b) return a;
    return gcd(b,a%b);
}
void solve(int x)
{
    int i,j,k,tx=1,ty=1;
    fo(i,1,x+1) fo(j,1,d-x+1) b[i][j]=0;
    fo(k,1,n*m/d)// (x+1)*(d-x+1)
    {
        fo(i,1,x+1) fo(j,1,d-x+1) f[i][j]=g[i][j]=0;
        f[x+1][d-x+1]=1-b[x+1][d-x+1];
        fd(i,x+1,1)
            fd(j,d-x+1,1)
            if (b[i][j]==0)
            {
                f[i-1][j]=(f[i-1][j]+f[i][j])%mo;
                f[i][j-1]=(f[i][j-1]+f[i][j])%mo;
            }
        fo(i,1,x+1) fo(j,1,d-x+1)
            b[i][j]|=a[tx+i-1][ty+j-1];
        tx+=x;
        if (tx>n) tx-=n;
        ty+=d-x;
        if (ty>m) ty-=m;
        g[1][1]=1-b[1][1];
        fo(i,1,x+1)
            fo(j,1,d-x+1)
            if (b[i][j]==0)
            {
                g[i+1][j]=(g[i+1][j]+g[i][j])%mo;
                g[i][j+1]=(g[i][j+1]+g[i][j])%mo;
                if (i!=1||j!=1||k==1) ans=(ans+1ll*g[i][j]*f[i][j])%mo;
            }
    }
}
void work()
{
    ans=0;
    scanf("%d %d\n",&n,&m);
    fo(i,1,n)
    {
        scanf("%s\n",s+1);
        fo(j,1,m) a[i][j]=a[i+n][j]=a[i+n][j+m]=a[i][j+m]=s[j]-'0';
    }
    d=gcd(n,m);
    fo(i,1,d)
        if (gcd(n,i)==1&&gcd(m,d-i)==1)
        {
            solve(i);
        }
    printf("%d\n",ans);
}
int main()
{
    freopen("robot.in","r",stdin);
    //freopen("robot.out","w",stdout);
    fo(i,0,100) c[i][0]=1;
    fo(i,1,100) fo(j,1,i)
        c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
    scanf("%d",&T);
    fo(TT,1,T)
        work();
}

你可能感兴趣的:(计数类问题)