码迷,mamicode.com
首页 > 其他好文 > 详细

[题解] Luogu P5641 【CSGRound2】开拓者的卓识

时间:2020-01-29 14:12:36      阅读:63      评论:0      收藏:0      [点我收藏+]

标签:can   const   amp   集合   bin   ++   就是   直接   spl   

这个柿子挺别致的......还有信仰膜数998244353


直接讲正解吧......

首先发现这个柿子从上往下算好像不怎么行,我们从下往上看,(下面令\(Ans_r = sum_{k,1,r}\))。

考虑\(a_i\)\(Ans_r\)的贡献(\(1 \le i \le r\)),即\(a_i\)\(sum_{k,1,r}\)中被算了多少次,我们假设\(a_i\)被算了\(c_i\)次。答案就是\(Ans_r = \sum\limits_{i = 1} ^ r a_i c_i\) 废话

这个\(c_i\)显然是位置\(i\)\([1,r]\)中被\(k-1\)重区间覆盖的方案数。

逼格高一点就是选\(k-1\)个(减一是因为最大的区间已经定下来是\([1,r]\)了)区间\([l_1,r_1],[l_2,r_2],\cdots,[l_{k-1},r_{k-1}]\),使得\(1 \le l_i,r_i \le r,[l_i, r_i] \subset [l_{i+1}, r_{i+1}]\)\(l_1 \le i \le r_1\)的方案数。

这个就是在\(1...i\)\(k-1\)个位置(可重复)的方案数\(\times\)\(i...r\)里选\(k-1\)个位置(可重复)的方案数。

\(c_i = s(i,k-1) s(r-i+1,k-1)\),其中\(s(n,m)\)表示在一个有\(n\)种元素,每种元素有无限个的集合内选\(m\)个元素出来做组合的方案数。

下面我们来推\(s\)

我们手里有一个有\(n\)个元素的集合,且每种元素有无限个。

实际上\(s(n,m)\)就等价于\(x_1 + x_2 + \cdots + x_n = m\)的非负整数解的个数,\(x_i\)表示第\(i\)种元素选了多少个。

经典的隔板法吧。这个好像小学奥数就有......

他就等价于这个集合的全排个数
\[ T = \{ m \cdot 1, (n-1) \cdot \text{|} \} \]
可以理解为把\(m\)\(1\)\((n-1)\)个板子隔开来,那么相邻两个板子之间,以及左右两边就代表了一个$x_1...x_n $

\(s(n,m) = \frac{(m+n-1)!}{m!(n-1)!} = \binom{n+m-1}{m}\)

那么\(c_i = \binom{i + k-2}{k-1}\binom{r-i+k-1}{k-1}\)

写回\(Ans_r\)就是
\[ Ans_r = \sum\limits_{i = 1}^{r} a_ic_i = \sum\limits_{i = 1} ^ r a_i \binom{i + k - 2}{k-1}\binom{r-i+k-1}{k-1} \]
这个柿子非常卷积吧!

我们令\(g_i = \binom{i + k - 1}{k - 1}\)\(f_i = \binom{i + k - 2}{k-1}a_i\),那么
\[ Ans_r = \sum\limits_{i = 1} ^ r f_ig_{r-i} \]
因为有信仰质数\(998244353\)\(NTT\)一下就没了。

还有由于\(k\)很大,我们可以递推出\(g\),然后用\(f_i = a_i g_{i-1}\)来算\(f\)

#include <bits/stdc++.h>
using namespace std;
const int N=3e5+10,P=998244353,G=3,IG=(P+1)/G;
inline int fpow(int a,int b){
    int ret=1; for (;b;b>>=1,a=1ll*a*a%P)
        if (b&1) ret=1ll*a*ret%P;
    return ret;
}
inline int add(int x,int y){return x+y>=P?x+y-P:x+y;}
inline int sub(int x,int y){return x-y<0?x-y+P:x-y;}
int rev[N];
void init(int limit){
    for (int i=0;i<limit;i++)rev[i]=rev[i>>1]>>1|((i&1)?limit>>1:0);
}
void ntt(int *f,int n,int flg){
    for (int i=0;i<n;i++) if (rev[i]<i)swap(f[i],f[rev[i]]);
    for (int len=2,k=1;len<=n;len<<=1,k<<=1){
        int wn=fpow(flg==1?G:IG,(P-1)/len);
        for (int i=0;i<n;i+=len){
            for (int w=1,j=i;j<i+k;j++,w=1ll*w*wn%P){
                int tmp=1ll*w*f[j+k]%P;
                f[j+k]=sub(f[j],tmp),f[j]=add(f[j],tmp);
            }
        }
    }
    if (flg==-1){
        int inv=fpow(n,P-2);
        for (int i=0;i<n;i++)f[i]=1ll*f[i]*inv%P;
    }
}
int f[N],g[N],a[N],inv[N];
int main(){
    int n,k; scanf("%d%d",&n,&k);
    for (int i=1;i<=n;i++)scanf("%d",&a[i]);
    inv[1]=1; for (int i=2;i<=n;i++) inv[i]=1ll*inv[P%i]*(P-P/i)%P; // 线性推逆元
    g[0]=1,g[1]=k%P; for (int i=2;i<=n;i++)
        g[i]=1ll*g[i-1]*inv[i]%P*(i+k-1)%P;  // 颓g
    for (int i=1;i<=n;i++) f[i]=1ll*a[i]*g[i-1]%P; // 颓f
    int limit=1; while(limit<=n*2)limit<<=1; init(limit);
    ntt(f,limit,1),ntt(g,limit,1);
    for (int i=0;i<limit;i++) f[i]=1ll*f[i]*g[i]%P;
    ntt(f,limit,-1);
    for (int i=1;i<=n;i++) printf("%d ",f[i]);
    return 0;
}

[题解] Luogu P5641 【CSGRound2】开拓者的卓识

标签:can   const   amp   集合   bin   ++   就是   直接   spl   

原文地址:https://www.cnblogs.com/wxq1229/p/12240153.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!