L_0_Forever_LF的专栏

一个热爱OI的OIer

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;
}
阅读更多
版权声明:...............转载说一声并注明出处qaq............... https://blog.csdn.net/L_0_Forever_LF/article/details/80658623
个人分类: UOJ DP 容斥原理
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭