Codeforces960G Bandit Blues

Problem

Codeforces

Solution

先找到序列中 \(n\) 的位置,那么在 \(n\) 之前必须有 \(a-1\) 个前缀最大值,之后有 \(b-1\) 个后缀最大值。

设 \(f[i][j]\) 表示长度为 \(i\) 的排列,有 \(j\) 个前缀最大值的方案数。

那么\(ans=\sum_{i=1}^n f[i-1][a-1]\times f[n-i][b-1]\times \binom n {i-1}\)

枚举最小值的位置,那么当且仅当它在第一个位置上时才会贡献一个前缀最大值,则 \(f[i][j]=f[i-1][j-1]+(i-1)f[i-1][j]\)。不难发现这其实是第一类斯特林数的递推式。

怎么理解呢?不妨记第 \(i\) 个前缀最大值的出现位置为 \(p_i\),把\([p_i,p_{i+1})\)视为一个盒子,盒子内的排列的方案数就是圆排列的方案数,所以这就是第一类斯特林数。

前后其实是一个对称的问题,那把它们放在一起考虑,答案就是 \(f[n-1][a+b-2]\binom {a+b-2} {b-1}\),即把这些盒子排在一起,然后再选 \(b-1\) 个盒子放到后面去。

第一类斯特林数可以用它的生成函数来做,倍增FFT加速即可做到\(O(n\log n)\)。

Code

#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=300010,mod=998244353,G=3;
template <typename Tp> int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> void read(Tp &x)
{
    x=0;char ch=getchar();int f=0;
    while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
    if(ch==‘-‘) ch=getchar(),f=1;
    while(ch>=‘0‘&&ch<=‘9‘) x=x*10+(ch-‘0‘),ch=getchar();
    if(f) x=-x;
}
int n,a,b,N,l,ans,fac[maxn],inv[maxn],rev[maxn],f[maxn],g[maxn];
int pw[maxn],ta[maxn],tb[maxn];
int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
int c(int n,int m){return m>n?0:(ll)fac[n]*inv[m]%mod*inv[n-m]%mod;}
int power(int x,int y)
{
    int res=1;
    for(;y;y>>=1,x=(ll)x*x%mod)
      if(y&1)
        res=(ll)res*x%mod;
    return res;
}
void NTT(int *a,int f)
{
    for(int i=1;i<N;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(int i=1;i<N;i<<=1)
    {
        int gn=power(G,(mod-1)/(i<<1));
        for(int j=0;j<N;j+=(i<<1))
        {
            int g=1,x,y;
            for(int k=0;k<i;++k,g=(ll)g*gn%mod)
            {
                x=a[j+k];y=(ll)g*a[j+k+i]%mod;
                a[j+k]=pls(x,y);a[j+k+i]=dec(x,y);
            }
        }
    }
    if(f==-1)
    {
        int inv=power(N,mod-2);reverse(a+1,a+N);
        for(int i=0;i<N;i++) a[i]=(ll)a[i]*inv%mod;
    }
}
void solve(int n)//x^n
{
    if(n==1){f[1]=1;return ;}
    int h=n>>1;solve(n>>1);
    for(N=1,l=0;N<=n;N<<=1) ++l;
    for(int i=1;i<N;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    for(int i=1;i<=h;i++) pw[i]=(ll)pw[i-1]*h%mod;
    for(int i=0;i<=h;i++) ta[i]=(ll)fac[i]*f[i]%mod;
    for(int i=0;i<=h;i++) tb[i]=(ll)pw[i]*inv[i]%mod;
    for(int i=h+1;i<N;i++) ta[i]=tb[i]=0;
    reverse(tb,tb+h+1);
    NTT(ta,1);NTT(tb,1);NTT(f,1);
    for(int i=0;i<N;i++) g[i]=(ll)ta[i]*tb[i]%mod;
    NTT(g,-1);
    for(int i=0;i<=h;i++) g[i]=(ll)g[i+h]*inv[i]%mod;
    for(int i=h+1;i<N;i++) g[i]=0;
    NTT(g,1);
    for(int i=0;i<N;i++) f[i]=(ll)f[i]*g[i]%mod;
    NTT(f,-1);
    if(n&1)
    {
        for(int i=n;i;i--) f[i]=pls(f[i-1],(ll)f[i]*(n-1)%mod);
        f[0]=(ll)f[0]*(n-1)%mod;
    }
}
int main()
{
    read(n);read(a);read(b);fac[0]=pw[0]=1;
    if(a<1||b<1){puts("0");return 0;}
    if(n==1){printf("%d\n",(a==1&&b==1));return 0;}
    for(int i=1;i<=n;i++) fac[i]=(ll)fac[i-1]*i%mod;
    inv[n]=power(fac[n],mod-2);
    for(int i=n-1;~i;i--) inv[i]=(ll)inv[i+1]*(i+1)%mod;
    solve(n-1);
    ans=(ll)f[a+b-2]*c(a+b-2,a-1)%mod;
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/totorato/p/10359105.html

时间: 2024-10-17 06:34:29

Codeforces960G Bandit Blues的相关文章

Codeforces 960G Bandit Blues

题目大意 链接:CF960G 给定正整数\(n\),表示有\(1\sim n\)个元素,求有多少种全排列满足: 从左往右按贪心原则去最大值,共取出\(a\)个元素:从右往左按贪心原则去最大值,共取出\(b\)个元素. 答案对\(998244353\)取模,数据满足\(1\leq n\leq 10^5,1\leq a,b\leq n\). 题目分析 我们先考虑一个递推做法. 设\(f(i,j)\)表示\(1\sim i\),按贪心原则会取\(j\)个数的方案数. 若第\(i\)个数为\(i\),则

@codeforces - [email&#160;protected] Bandit Blues

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 求有多少个长度为 n 的排列,从左往右遍历有 a 个数比之前遍历的所有数都大,从右往左遍历有 b 个数比之前遍历的所有数都大. 模 998244323. input 一行三个整数 n

Divide by Zero 2018 and Codeforces Round #474 (Div. 1 + Div. 2, combined)G - Bandit Blues

题意:求满足条件的排列,1:从左往右会遇到a个比当前数大的数,(每次遇到更大的数会更换当前数)2.从右往左会遇到b个比当前数大的数. 题解:1-n的排列,n肯定是从左往右和从右往左的最后一个数. 考虑\(S(n,m)\)是1-n排列中从左往右会遇到m个比当前数大的数,考虑把1放在最左边,即\(S(n-1,m-1)\),考虑1不在最左边,有n-1个位置,1不可能会更换\((n-1)*S(n,m)\).即\(S(n,m)=S(n-1,m-1)+(n-1)*S(n-1,m)\) \(S(n,m)\)即

组合计数小练

组合数学什么的,最有趣了呢-- [51nod 1251] Fox序列的数量 题意 求满足以下条件的序列数目: 序列长度为 $ n $ ,每个元素都属于 $ [1,m] \cap Z $ : 这个序列单调不降: 这个序列出现次数最多的数是唯一的. 数据范围: $ 1≤n,m≤100000 $ ,答案对 $ 1e9+7 $ 取模 题解 先枚举出现次数最多的数的出现次数 $ k $ ,我们要计算的是 $ x_1+x_2+...+x_{m-1}=n-k, ; x_i≤k-1 $ 的非负整数解数目. 可以

Multi-armed Bandit Problem与增强学习的联系

选自<Reinforcement Learning: An Introduction>, version 2, 2016, Chapter2 https://webdocs.cs.ualberta.ca/~sutton/book/bookdraft2016sep.pdf 引言中是这样引出Chapter2的: One of the challenges that arise in reinforcement learning, and not in other kinds of learning

Bandit

CSE599:online and adaptive machine learning Lecture 3:Stochastic Multi-Armed Bandits, Regret Minimization csdn  bandit 算法(3) -- UCB算法 推荐系统的EE问题及Bandit算法 https://x-algo.cn/index.php/2016/12/15/ee-problem-and-bandit-algorithm-for-recommender-systems/ h

bandit系列21--27

level21:linux计划任务 ls /etc/cron.d/    #发现可疑文件cronjob_bandit22 cat cronjob_bandit22    #发现执行命令/usr/bin/cronjob_bandit22.sh,查看该文件属性,发现group组有x权限. /usr/bin/cronjob_bandit22.sh cat t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv    #这里注意,用户没有ls /tmp的权限,但可以访问tmp里面的文件. le

Bandit Wargame Level12 Writeup

Level Goal The password for the next level is stored in the file data.txt, which is a hexdump of a file that has been repeatedly compressed. For this level it may be useful to create a directory under /tmp in which you can work using mkdir. For examp

OverTheWire Bandit

#sshpass is a helpful tool to enter password which could save tons of time. ssh [email protected]5koReBOKuIDDepwhWk7jZC0RTdopnAYKh6DXjZPULLxYr17uwoI01bNLQbtFemEgo77HKBPTKQnIay4Fw76bEy8PVxKEDQRKTzs8cvX2JJa4CFALtqS87jk27qwqGhBM9plV9sort data.txt |uniq