[期望] UOJ #214. 【UNR #1】合唱队形

首先考虑如果要完成指定 k 个课程,那么期望应该是 Fk=k1i=01ki
ft 表示 t 时刻之前 要求未被完成的概率
pS,t 表示 t 时刻之前 位置集合 S 未被完成的概率
然后瘦腿一发

E======t=0ftt=0S(1)|S|(1pS,t)t=0S(1)|S|pS,tS(1)|S|t=0pS,tS(1)|S|E(S)S(1)|S|Fk(S)

这样复杂度是 2nm+1 乘上一些 nm 什么的什么我忘了
可以解决 nm 较小的情况

然后剩下的情况 我们直接计算 Fk 的贡献
dpi,s,k 表示做到第 i 个,第 i 个需要学会集合 s,当前总共已经需要 k 个课程的贡献,然后决策这一个位置选不选入 S 来转一下
这样复杂度是 2m 乘上一些 nm 什么的什么我忘了

然后就可以了

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;

#define read(x) scanf("%d",&(x))

const int P=998244353;
const int N=32;

inline ll Pow(ll a,int b){
  ll ret=1; for (;b;b>>=1,a=a*a%P) if (b&1) ret=ret*a%P; return ret;
}
ll inv[1005];
inline void Pre(){
  inv[1]=1; for (int i=2;i<=1000;i++) inv[i]=(ll)(P-P/i)*inv[P%i]%P;
}
inline ll Inv(ll a){
  //return Pow(a,P-2);
  return inv[a];
}


int n,m,tot;
char t[N][N]; int len[N];
char s[N];

inline bool check(){
  for (int i=1;i<=n-m+1;i++){
    int flag=0;
    for (int j=i;j<=i+m-1;j++)
      for (int k=1;k<=len[j];k++)
    if (t[j][k]==s[j-i+1])
      flag++;
    if (flag==m) return 1;
  }
  return 0;
}

ll F[N*N];

bool b[N][26],a[N][26];

int tmp[N],pnt;
int g[N];
int tag[N];

inline void Solve1(){
  ll ans=0;
  pnt=0;
  for (int i=1;i<=n;i++) if (tag[i]) tmp[++pnt]=i;
  for (int x=0;x<(1<<pnt);x++){
    cl(a); int c=0,k=0;
    for (int i=1;i<=pnt;i++)
      if (x>>(i-1)&1){
    int p=tmp[i];
    for (int j=p;j<=p+m-1;j++){
      k+=a[j][s[j-p+1]-'a']==0;
      a[j][s[j-p+1]-'a']=1;
    }
    c++;
      }
    if (c&1)
      ans+=P-F[k];
    else
      ans+=F[k];
  }
  ans=(P-ans%P)%P;
  printf("%lld\n",ans);
}

int f[2][1<<15][1005];
int cnt[1<<15];
int vst[26],clk;
int tim;

inline void Solve2(){
  for (int i=1;i<=n;i++) len[i]+=len[i-1];
  for (int j=0;j<(1<<m);j++){
    ++clk; int c=0;
    for (int i=0;i<m;i++)
      if (j>>i&1)
    if (vst[s[i+1]-'a']!=clk)
      c++,vst[s[i+1]-'a']=clk;
    cnt[j]=c;
  }
  int t=0;
  f[t][0][0]=1;
  for (int i=0;i<n;i++,t^=1)
    for (int j=0;j<(1<<m);j++)
      for (int k=0;k<=len[i];k++)
    if (f[t][j][k]){
      ++tim;
      int s=(j<<1)&((1<<m)-1);
      if (tag[i+1])
        (f[t^1][s|1][k+cnt[s|1]]+=P-f[t][j][k])%=P;
      (f[t^1][s][k+cnt[s]]+=f[t][j][k])%=P;
      f[t][j][k]=0;
    }
  ll ans=0;
  for (int k=0;k<=len[n];k++){
    ll c=0;
    for (int j=0;j<(1<<m);j++)
      if (f[t][j][k])
    c+=f[t][j][k],f[t][j][k]=0;
    ans+=F[k]*(c%P)%P;
  }
  ans=(P-ans%P)%P;
  printf("%lld\n",ans);
}

int main(){
  int T;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(T); Pre();
  while (T--){
//     tim=0;
    read(n); read(m); tot=0; cl(b);
    for (int i=1;i<=n;i++) scanf("%s",t[i]+1),len[i]=strlen(t[i]+1),tot+=len[i];
    scanf("%s",s+1);
    for (int i=1;i<=n;i++) for (int j=1;j<=len[i];j++) b[i][t[i][j]-'a']=1;
    cl(g);
    for (int i=1;i<=n;i++)
      for (int j=0;j<m;j++)
    if (b[i][s[j+1]-'a'])
      g[i]+=1<<j;
    cl(tag);
    for (int i=1;i<=n-m+1;i++){
      tag[i]=1;
      for (int j=1;j<=m;j++)
    tag[i]&=(g[i+j-1]>>(j-1)&1);
    }
    for (int k=0;k<=tot;k++){
      F[k]=0;
      for (int i=0;i<k;i++)
    F[k]+=(ll)tot*Inv(k-i)%P;
      F[k]%=P;
    }
    if (!check()) { printf("-1\n"); continue; }
    if (n-m+1<=17)
      Solve1();
    else
      Solve2();
//    fprintf(stderr,"%d\n",tim);
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值