【Codeforces960G】 Bandit Blues

7 篇文章 0 订阅
1 篇文章 0 订阅

time limit per test:3.5 seconds
memory limit per test:256 megabytes
standard input
standard output

Description
Japate, while traveling through the forest of Mala, saw N bags of gold lying in a row. Each bag has some distinct weight of gold between 1 to N. Japate can carry only one bag of gold with him, so he uses the following strategy to choose a bag.

Initially, he starts with an empty bag (zero weight). He considers the bags in some order. If the current bag has a higher weight than the bag in his hand, he picks the current bag.

Japate put the bags in some order. Japate realizes that he will pick A bags, if he starts picking bags from the front, and will pick B bags, if he starts picking bags from the back. By picking we mean replacing the bag in his hand with the current one.

Now he wonders how many permutations of bags are possible, in which he picks A bags from the front and B bags from back using the above strategy.

Since the answer can be very large, output it modulo 998244353.

Input
The only line of input contains three space separated integers N(1N105) N ( 1   ≤   N   ≤   10 5 ) , A and B (0 ≤ A, B ≤ N).

Output
Output a single integer — the number of valid permutations modulo 998244353.

Examples
input1

1 1 1

output1

1

input2

2 1 1

output2

0

input3

2 2 1

output3

1

input4

5 2 2

output4

22

Note
In sample case 1, the only possible permutation is [1]

In sample cases 2 and 3, only two permutations of size 2 are possible:{[1, 2], [2, 1]}. The values of a and b for first permutation is 2 and 1, and for the second permutation these values are 1 and 2.

In sample case 4, out of 120 permutations of [1, 2, 3, 4, 5] possible, only 22 satisfy the given constraints of a and b.


题目大意
对于一个n的排列,按顺序去做一个寻找最大值的过程:找到一个比当前的数更大的就替换掉。
正着做要替换A次,倒着做要替换B次,求排列种数


因为A次的最后和B次的最后一定是N,那么考虑以n为界分成前后两部分
Sn,k S n , k 表示n的排列,正着做可以替换k次的排列数
显然 Ans=n1i=1Si,A1Sni1,B1Cini A n s = ∑ i = 1 n − 1 S i , A − 1 S n − i − 1 , B − 1 C n − i i
考虑S的递推式
将n-1的排列整体加1,再将1放进去,对k造成的影响只取决于1放在第一位还是后面,因此

Sn,k=Sn1,k1+(n1)Sn1,k S n , k = S n − 1 , k − 1 + ( n − 1 ) S n − 1 , k

显然 Sn1,k=[n1k] S n − 1 , k = [ n − 1 k ] 第一类斯特林数

考虑它的定义,将1~n放入k个圆排列的方案数
那么答案可以看做将i个数放入A-1个圆排列再将n-i-1个数放入B-1个圆排列的方案数
因此可以合并为n-1个数中取A+B-2个组成圆排列取其中A-1个与B-1个
Ans=Sn1,A+B2CA1A+B2 ∴ A n s = S n − 1 , A + B − 2 ∗ C A + B − 2 A − 1

再看斯特林数的特征式
Sn,k=xn[xk] S n , k = x n ↑ [ x k ] xn=n1i=0xi=ni=0Sn,ixi x n ↑ = ∏ i = 0 n − 1 x − i = ∑ i = 0 n S n , i x i

用分治+NTT求出各项系数即可

#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
#define N 262144
#define mo 998244353

using namespace std;

int n,A,B;
ll w[N],y[N],a[20][N],b[20][N],p[N],q[N];

ll qpow(ll a,ll i){
    ll r=1;for(;i;i>>=1,a=a*a%mo)if(i&1)r=r*a%mo;return r;
}

void ntt(ll *a,int sig,int l){
    ll ny=qpow(l,mo-2);
    for(int i=0;i<l;i++)y[i]=a[i];
    for(int i=0,p=0;i<l;i++){
        if(i<p)swap(y[i],y[p]);
        for(int j=l>>1;(p^=j)<j;j>>=1);
    }
    for(int hf=1,m=2;hf<l;hf=m,m<<=1)for(int i=0;i<hf;i++){
        ll W=w[(i*sig+m)%m*(N/m)];
        for(int j=i;j<l;j+=m){
            int k=j+hf;ll u=y[j],v=y[k]*W%mo;
            y[j]=(u+v)%mo;y[k]=(u+mo-v)%mo;
        }
    }
    for(int i=0;i<l;i++)a[i]=y[i];
    if(sig<0)for(int i=0;i<l;i++)a[i]=a[i]*ny%mo;
}
void tr(ll *a,ll *b,ll *c,int l){
    for(int i=0;i<l;i++)p[i]=a[i],q[i]=b[i];ntt(p,1,l);ntt(q,1,l);
    for(int i=0;i<l;i++)c[i]=p[i]*q[i]%mo;ntt(c,-1,l);
}
void dg(ll *c,int h,int t,int dep){
    if(h>t){c[0]=1;return;}
    if(h==t){for(int i=0;i<1<<18-dep+1;i++)c[i]=0;c[0]=h;c[1]=1;return;}
    int m=h+t>>1;dg(a[dep],h,m,dep+1);dg(b[dep],m+1,t,dep+1);
    tr(a[dep],b[dep],c,1<<18-dep+1);
}

int main(){
    ll wn=qpow(3,(mo-1)/N);
    w[0]=1;for(int i=1;i<N;i++)w[i]=w[i-1]*wn%mo;
    scanf("%d %d %d",&n,&A,&B);
    if(!A || !b){printf("0");return 0;}
    dg(a[0],0,n-2,1);ll ans=a[0][A+B-2];
    for(int i=1;i<=A-1;i++)ans=ans*qpow(i,mo-2)%mo*(ll)(A+B-1-i)%mo;
    printf("%I64d",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值