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

我們令fi 表示使得i ~i+L1 合法的期望次數,題目要求的其實就是min(f1,f2....fnL+1) 的期望

我們先考慮怎麼求fi ,設共有U 種課程,其中有L 個課程是要上的,相當於U 個白球,其中有L 個球有標記,每次我們隨機取出一個球將他染黑,問將這L 個標記球都染黑的期望次數
我們設hi 表示我們已經染黑了i 個標記球的期望次數,有
hi+1=1+kiUhi+Uk+iUhi+1

可解得hL=i=1LUi

然後,最大最小值反演這個東西是可以套在期望上的
直接求這個min不好求,但是求max就相當於我們上面求的這個給球染色的期望次數
gS 表示使集合S裏的所有區間都合法,需要上的課程數
ans=Si=1gSUi
複雜度O(2nLn) ,當n-L較大的時候會GG

注意到當L 比較小的時候,我們可以不枚舉S,直接對整個序列dp,dp[i][m][mask] 表示dp到第i個位置,當前確定要上的課程有m個,前L 個區間是否被選的狀態爲mask,方案數

複雜度O(n32L) ,和前面那個算法結合起來就可以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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章