UOJ#214. 【UNR #1】合唱队形

我们令 f i 表示使得 i ~ i + L 1 合法的期望次数,题目要求的其实就是 m i n ( f 1 , f 2 . . . . f n L + 1 ) 的期望

我们先考虑怎么求 f i ,设共有 U 种课程,其中有 L 个课程是要上的,相当于 U 个白球,其中有 L 个球有标记,每次我们随机取出一个球将他染黑,问将这 L 个标记球都染黑的期望次数
我们设 h i 表示我们已经染黑了 i 个标记球的期望次数,有
h i + 1 = 1 + k i U h i + U k + i U h i + 1

可解得 h L = i = 1 L U i

然后,最大最小值反演这个东西是可以套在期望上的
直接求这个min不好求,但是求max就相当于我们上面求的这个给球染色的期望次数
g S 表示使集合S里的所有区间都合法,需要上的课程数
a n s = S i = 1 g S U i
复杂度 O ( 2 n L n ) ,当n-L较大的时候会GG

注意到当 L 比较小的时候,我们可以不枚举S,直接对整个序列dp, d p [ i ] [ m ] [ m a s k ] 表示dp到第i个位置,当前确定要上的课程有m个,前 L 个区间是否被选的状态为mask,方案数

复杂度 O ( n 3 2 L ) ,和前面那个算法结合起来就可以AC了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;

const int maxn = 35;
const int maxm = 35;
const int mask = 1<<10;
const int mod = 998244353;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;if(a<0)a+=mod;}

int pw(int x,int k)
{
    int re=1;
    for(;k;k>>=1,x=(ll)x*x%mod) if(k&1)
        re=(ll)re*x%mod;
    return re;
}
int inv(int x){ return pw(x,mod-2); }

int n,m,num,u;
int s[maxn*maxn];
int v[maxn][30];
int M[maxm],mh[maxn];

bool Check()
{
    int ok=0;
    for(int i=1;i<=u;i++)
    {
        int nk=1;
        for(int j=0;j<m;j++) if(!v[i+j][M[j]]) { nk=0;break; }
        mh[i]=nk;
        ok|=nk;
    }
    return ok;
}
int ans;
int use[maxn][30];
void dfs(int nowi,int k,int sig)
{
    if(nowi>u) { sig==1?add(ans,s[k]):add(ans,-s[k]); return; }
    dfs(nowi+1,k,sig);
    if(!mh[nowi]) return;
    for(int j=0;j<m;j++)
    {
        if(!use[nowi+j][M[j]]) k++;
        use[nowi+j][M[j]]++;
    }
    dfs(nowi+1,k,-sig);
    for(int j=0;j<m;j++) use[nowi+j][M[j]]--;
}
int f[2][mask][maxn*maxn];
void dp()
{
    int al=1<<m-1;
    memset(f,0,sizeof f);
    int now=0; f[now][0][0]=-1;
    for(int i=1;i<=n;i++)
    {
        now=!now;
        for(int j=0;j<al;j++) 
        {
            int tc=0,tn=0;
            for(int k=0;k<m-1;k++) if(j>>k&1)
                if(i-m+k+1>0)
                {
                    int cc=M[m-k-1];
                    if(!(tc>>cc&1)) tc|=1<<cc,tn++;
                }
            for(int k=0;k<=num;k++) if(f[!now][j][k])
            {
                int &temp=f[!now][j][k];
                add(f[now][j>>1][k+tn],temp);
                if(i<=u&&mh[i]) add(f[now][j>>1|(m-2>=0?(1<<m-2):0)][k+tn+!(tc>>M[0]&1)],-temp);
                temp=0;
            }
        }
    }
    for(int j=0;j<al;j++) for(int k=0;k<=num;k++)
        add(ans,(ll)f[now][j][k]*s[k]%mod);
}

char str[maxn];

int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);

    int tcase; scanf("%d",&tcase);
    while(tcase--)
    {
        memset(v,0,sizeof v); num=0;
        scanf("%d%d",&n,&m); u=n-m+1;
        for(int i=1;i<=n;i++)
        {
            scanf("%s",str); int len=strlen(str);
            num+=len;
            for(int j=0;j<len;j++) v[i][str[j]-'a']=1;
        }
        scanf("%s",str);
        for(int i=0;i<m;i++) M[i]=str[i]-'a';
        if(!Check()) { puts("-1"); continue; }

        for(int i=1;i<=num;i++) s[i]=(s[i-1]+(ll)num*inv(i)%mod)%mod;

        ans=0;
        if(m<=10) dp();
        else dfs(1,0,-1);
        printf("%d\n",ans);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/l_0_forever_lf/article/details/80658623