TimusOJ 1476. Lunar Code

题意:给你 n , m n,m n,m,让你求满足对于每一列 j j j,满足 a [ i ] [ j ] = 0 , a [ i ] [ j + 1 ] = 1 a[i][j]=0,a[i][j+1]=1 a[i][j]=0,a[i][j+1]=1 i i i不超过 k k k个的01矩阵的个数。

Solution

我的思路一开始跑偏了。
我把满足 a [ i ] [ j ] = 0 , a [ i ] [ j + 1 ] = 1 a[i][j]=0,a[i][j+1]=1 a[i][j]=0,a[i][j+1]=1的点设置为 1 1 1,其他的点我设置为 0 0 0,然后我尝试以行为状态,但是这样的状态数高达一亿多,还要记录每一行满足条件的个数,感觉行不通,然后我有尝试以列为状态,这样可以不用在dp中记录k,感觉好一些,不过我发现计数的时候要统计每一行的1和改行前面的1的距离,其实这和以行为状态计算没有什么不同。后面我想起【CSP2019Emiya 家今天的饭】里面的补集转化,但是我思考很久以后发现也不行,我就被卡住了。
我有想了几个小时,突然灵机一动,感觉我不应该把满足 a [ i ] [ j ] = 0 , a [ i ] [ j + 1 ] = 1 a[i][j]=0,a[i][j+1]=1 a[i][j]=0,a[i][j+1]=1的点设置为 1 1 1,因为这样不方便统计,所以我尝试直接用矩阵来做状态,其中很明显的是不能用行来当作状态,所以只能用列来做状态。我有仔细想想,对于列来说,转移的时候貌似之关注1的个数,并不在意1,0的具体位置,所以我令 f [ i ] [ j ] f[i][j] f[i][j]表示第 i i i列0的个数为 j j j的方案数,预处理组合数以后直接暴力转移就好了。

Code

打代码时因为高精度乘高精度打错导致调了很久。。。
幸运的是0.98s刚好卡过去了。。。

#include<bits/stdc++.h>
#define ll long long
#define gc getchar
#define pc putchar
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
using namespace std;
template<typename T>inline void qr(T &x){
  x=0;int f=0;char s=gc();
  while(!isdigit(s))f|=s=='-',s=gc();
  while(isdigit(s))x=x*10+s-48,s=gc();
  x=f?-x:x;
}
int cc=0,buf[31];
template<typename T>inline void qw(T x){
  if(x<0)pc('-'),x=-x;
  do{buf[++cc]=int(x%10);x/=10;}while(x);
  while(cc)pc(buf[cc--]+'0');puts("");
}
const int N=42,md=1e4;
struct node{
  int len,a[150];
  node(){len=0;memset(a,0,sizeof(a));}
};
int n,m,k;
node c[N][N],f[N][N],ans;
node zh(int y){
  node x;
  while(y){
    x.a[++x.len]=y%md;
    y/=md;
  }
  return x;
}
node operator+(node x,node y){
  node z;z.len=max(x.len,y.len);
  rep(i,1,z.len){
    z.a[i]+=x.a[i]+y.a[i];
    z.a[i+1]+=z.a[i]/md;
    z.a[i]%=md;
  }
  if(z.a[z.len+1])z.len++;
  return z;
}
node operator*(node x,node y){
  node z;
  if(!x.len||!y.len)return z;
  z.len=x.len+y.len-1;  
  rep(i,1,x.len)
    rep(j,1,y.len)
      z.a[i+j-1]+=x.a[i]*y.a[j];
  rep(i,1,z.len){
    z.a[i+1]+=z.a[i]/md;
    z.a[i]%=md;
    if(z.a[z.len+1])z.len++;
  }
  return z;
}
void prt(node x){
  dwn(i,x.len,1){
    if(i==x.len)printf("%d",x.a[i]);
    else{
      for(int j=100;j<=md;j*=10)
        if(x.a[i]<j/10)pc('0');
      printf("%d",x.a[i]);
    }
  }
}
int main(){
  qr(n),qr(m),qr(k);
  c[0][0]=zh(1);
  rep(i,1,n){
    c[i][0]=zh(1);
    rep(j,1,i)c[i][j]=c[i-1][j-1]+c[i-1][j];
  }
  f[0][0]=zh(1);
  rep(ki,1,1)
    rep(i,0,n){
      int now=n-i;
      rep(j,0,n)
        rep(l,0,j){
          if(l>now||l>k)break;
          f[ki][i]=f[ki][i]+f[ki-1][j]*c[j][l]*c[n-j][now-l];
        }
    }
  rep(ki,2,m)
    rep(i,0,n){
      int now=n-i;
      rep(j,0,n)
        rep(l,0,j){
          if(l>now||l>k)break;
          f[ki][i]=f[ki][i]+f[ki-1][j]*c[j][l]*c[n-j][now-l];
        }
    }
  rep(i,0,n)
    ans=ans+f[m][i];
  prt(ans);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值