CF960G Bandit Blues

题目链接

题意分析

我们考虑将已经维护好了\(2∽n\)的数列现在考虑插入最小值

然后现在插入最小值\(1\)

现在我们使用\(dp(i,j)\)表示前\(i\)个数一共存在\(j\)个前缀最大值的方案数

我们如果放在开头 那么贡献就是\(dp(i-1,j-1)\)

如果放在别的位置就是\(dp(i-1,j)\)

所以

\[dp(i,j)=dp(i-1,j-1)+(i-1)* dp(i-1,j) \]

等一下 这就是第一类\(Strling\)

\[S(n,m)=S(n-1,m-1)+(n-1)* S(n-1,m) \]

然后我们考虑怎么计算题目要求的

首先我们考虑对于一个序列\(i\)存在\(j\)个前缀最小大值

翻转之后就是存在\(j\)个后缀最大值

所以我们考虑当前最大值\(n\)插入的位置

那么一定是左边存在\(a-1\)个前缀最大值 右边存在\(b-1\)个后缀最大值

那么答案就是

\[ans=\sum_{i=a-1}^{n-b}dp(i,a-1)* dp(n-i-1,b-1)* C_{n-1}^i \]

但是我们无须枚举

考虑当前是\(dp(n-1,a+b-2)\)

我们将最大值\(n\)插入第\(a-1\)个之后

然后剩余的\(b-1\)到后面然后翻转 就成了我们要的序列

所以就是

\[ans=dp(n-1,a+b-2)* C_{a+b-2}^{a-1} \]

关于第一类\(Strling\)

\[x(x+1)(x+2)......(x+n-1)=\sum_{i=0}^nS(n,i)x^i \]

所以我们可以使用分治\(NTT\)

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<string>
#include<queue>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<deque>
#include<vector>
#include<ctime>
#define ll long long
#define inf 0x7fffffff
#define N 400008
#define IL inline
#define M 1008611
#define Gi 332748118
#define mod 998244353
#define R register
using namespace std;
template<typename T>IL void read(T &_)
{
    T __=0,___=1;char ____=getchar();
    while(!isdigit(____)) {if(____=='-') ___=0;____=getchar();}
    while(isdigit(____)) {__=(__<<1)+(__<<3)+____-'0';____=getchar();}
    _=___ ? __:-__;
}
/*-------------OI使我快乐-------------*/
int n,cdy,wzy;
ll ans[20][N],key[N];
IL ll qpow(ll x,ll y)
{ll res=1;for(;y;y>>=1,x=x*x%mod) if(y&1) res=res*x%mod;return res;}
IL ll C(ll x,ll y)
{
    ll res1=1,res2=1;
    for(R ll i=x-y+1;i<=x;++i) res1=res1*i%mod;
    for(R ll i=1;i<=y;++i) res2=res2*i%mod;
    return res1*qpow(res2,mod-2)%mod;
}
IL void NTT(ll *A,int lim,int knd)
{
    for(R int i=0;i<lim;++i) if(i<key[i]) swap(A[i],A[key[i]]);
    for(R int mid=1;mid<lim;mid<<=1)
    {
        ll Wn=qpow(knd==1 ? 3:Gi,(mod-1)/(mid<<1));
        for(R int j=0;j<lim;j+=(mid<<1))
        {
            ll w=1;
            for(R int k=0;k<mid;++k,w=w*Wn%mod)
            {
                ll x=A[j+k],y=w*A[j+k+mid]%mod;
                A[j+k]=(x+y)%mod;A[j+k+mid]=(x-y+mod)%mod;
            } 
        }
    }
    if(knd==-1)
    {
        ll inv=qpow(lim,mod-2);
        for(R int i=0;i<lim;++i) A[i]=A[i]*inv%mod;
        return;
    }
}
IL void solve(int si,int le,int ri)
{
    if(le==ri) {ans[si][0]=le;ans[si][1]=1;return;}	
    int mid=(le+ri)>>1;
    solve(si+1,le,mid);
    for(R int i=0;i<=mid-le+1;++i) ans[si][i]=ans[si+1][i];
    solve(si+1,mid+1,ri);
    
    int all=0,lim=0;
    for(lim=1;lim<=(ri-le+1)+2;lim<<=1) ++all;
    for(R int i=0;i<lim;++i) key[i]=((key[i>>1]>>1)|((i&1)<<(all-1)));
    for(R int i=mid-le+2;i<lim;++i) ans[si][i]=0;
    for(R int i=ri-mid+1;i<lim;++i) ans[si+1][i]=0;
    NTT(ans[si],lim,1);NTT(ans[si+1],lim,1);
    for(R int i=0;i<lim;++i) ans[si][i]=ans[si][i]*ans[si+1][i]%mod;
    NTT(ans[si],lim,-1);
}
int main()
{
//	freopen("lzxkj.in","r",stdin);
//	freopen("lzxkj.out","w",stdout);
    read(n);read(cdy);read(wzy);
    if(!cdy||!wzy||cdy+wzy-1>n) {puts("0");return 0;}
    if(n==1) {puts("1");return 0;}
    solve(0,0,n-2);
    printf("%lld\n",ans[0][cdy+wzy-2]*C(cdy+wzy-2,cdy-1)%mod);
//	fclose(stdin);
//	fclose(stdout);
    return 0;
}




HEOI 2019 RP++

posted @ 2019-03-24 21:24  tcswuzb  阅读(231)  评论(0)    收藏  举报