zoukankan      html  css  js  c++  java
  • [题解] Luogu P5641 【CSGRound2】开拓者的卓识

    这个柿子挺别致的......还有信仰膜数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 = sumlimits_{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)个位置(可重复)的方案数( imes)(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 ext{|} } ]

    可以理解为把(m)(1)((n-1))个板子隔开来,那么相邻两个板子之间,以及左右两边就代表了一个$x_1...x_n $

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

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

    写回(Ans_r)就是

    [Ans_r = sumlimits_{i = 1}^{r} a_ic_i = sumlimits_{i = 1} ^ r a_i inom{i + k - 2}{k-1}inom{r-i+k-1}{k-1} ]

    这个柿子非常卷积吧!

    我们令(g_i = inom{i + k - 1}{k - 1})(f_i = inom{i + k - 2}{k-1}a_i),那么

    [Ans_r = sumlimits_{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;
    }
    
  • 相关阅读:
    tar解压出错
    HUNNU11352:Digit Solitaire
    cocos2d-x 二进制文件的读写
    电子支付概述(1)
    新一批思科电子书下载
    HUNNU11354:Is the Name of This Problem
    POJ 3181 Dollar Dayz 简单DP
    Spring中IOC和AOP的详细解释
    atheros wifi 动因分析
    Android ActionBar相关
  • 原文地址:https://www.cnblogs.com/wxq1229/p/12240153.html
Copyright © 2011-2022 走看看