CF1251F-Red-White Fence【NTT】

前言

刚开始看错题推了半天的生成函数
在这里插入图片描述


正题

题目链接:https://www.luogu.com.cn/problem/CF1251F


题目大意

\(n\)个白色木板,\(k\)个红色木板,给出这些木板的高度,木板排成一排形成栅栏。栅栏要求只有一个红色木板且在红色木板左边单调升,右边单调降。

\(m\)次询问能够围成周长为\(q_i\)有多少种围法。


解题思路

首先如果栅栏多余两个可以看做是两个,因为同一个高度的栅栏最多只能出现两次,而木板相同。

因为\(k\)很小显然是要我们处理\(k\)次,现在分开考虑出现两次的和出现一次的方案。若出现一次的栅栏有\(x\)个,拿出\(k\)围个的方案数就是\(\binom{x}{k}*2^k\),若出现两次的栅栏有\(y\)个,拿出\(k\)个围的方案数就是\(\binom{k}{2x}\)

然后两种方案卷起来就可以计算答案了。

时间复杂度\(O(\ k(n\log n+m)\ )\)


\(code\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=(12*1e5)+10,P=998244353;
struct poly{
    ll a[N],n;
}F,G;
ll n,m,t,f1,f2,r[N],a[N],v[N],b[N],f[N],pw[N],fac[N],inv[N],q[N],ans[N];
ll power(ll x,ll b){
    ll ans=1;
    while(b){
        if(b&1)ans=ans*x%P;
        x=x*x%P;b>>=1;
    }
    return ans;
}
void NTT(ll *f,ll n,ll op){
    for(ll i=0;i<n;i++)
        if(r[i]<i)swap(f[r[i]],f[i]);
    for(ll p=2;p<=n;p<<=1){
        ll len=p>>1;
        ll tmp=power(3,(P-1)/p);
        if(op==-1)tmp=power(tmp,P-2);
        for(ll k=0;k<n;k+=p){
            ll buf=1;
            for(ll i=k;i<k+len;i++){
                ll tt=f[i+len]*buf%P;
                f[i+len]=(f[i]-tt+P)%P;
                f[i]=(f[i]+tt)%P;
                buf=buf*tmp%P;
            }
        }
    }
    if(op==-1){
        ll invn=power(n,P-2);
        for(ll i=0;i<n;i++)
            f[i]=f[i]*invn%P;
    }
    return;
}
void mul(poly &a,poly &b){
    ll n=1;
    while(n<=a.n+b.n)n<<=1;
    for(ll i=0;i<n;i++)
        r[i]=(r[i>>1]>>1)^((i&1)?(n>>1):0);
    NTT(a.a,n,1);NTT(b.a,n,1);
    for(ll i=0;i<n;i++)
        a.a[i]=a.a[i]*b.a[i]%P;
    NTT(a.a,n,-1);return;
}
ll C(ll n,ll m)
{return fac[n]*inv[m]%P*inv[n-m]%P;}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    fac[0]=pw[0]=inv[0]=1;
    for(ll i=1;i<=4*n;i++)
        fac[i]=fac[i-1]*i%P,pw[i]=pw[i-1]*2%P;
    inv[4*n]=power(fac[4*n],P-2);
    for(ll i=4*n;i>1;i--)
        inv[i-1]=inv[i]*i%P;
    for(ll i=1;i<=m;i++)
        scanf("%lld",&b[i]);
    sort(b+1,b+1+m);
    scanf("%lld",&t);
    for(ll i=1;i<=t;i++)
        scanf("%lld",&q[i]);
    sort(a+1,a+1+n);ll l=1;
    for(ll k=1;k<=m;k++){
        while(l<=n&&a[l]<b[k]){
            if(!v[a[l]])f1++;
            else if(v[a[l]]==1)f1--,f2++;
            v[a[l]]++;l++;
        }
        memset(G.a,0,sizeof(G.a));
        memset(F.a,0,sizeof(F.a));
        for(ll i=0;i<=f1;i++)
            G.a[i]=pw[i]*C(f1,i)%P;
        G.n=f1+1;
        for(ll i=0;i<=2*f2;i++)
            F.a[i]=C(2*f2,i);
        F.n=2*f2+1;
        mul(G,F);
        for(ll i=1;i<=t;i++)
            if(q[i]>=b[k]*2+2)
                ans[i]=(ans[i]+G.a[q[i]/2-b[k]-1])%P;
    }
    for(ll i=1;i<=t;i++)
        printf("%lld\n",ans[i]);
    return 0;
}
posted @ 2021-01-05 16:51  QuantAsk  阅读(52)  评论(0编辑  收藏  举报