【学习笔记】[AHOI2022] 山河重整

学习自 E I EI EI的题解。

生成函数真是太强大辣!!!

这题的切入点非常有意思。记 a k + 1 a_{k+1} ak+1表示和为 k k k,且能表达出 1 ∼ k 1\sim k 1k的集合的数量,那么我们得出一个非常巧妙的方程

x ( 1 + x ) ( 1 + x 2 ) . . . . ( 1 + x n ) = ∑ k a k x k ( 1 + x k + 1 ) . . . ( 1 + x n ) x(1+x)(1+x^2)....(1+x^n)=\sum_k a_{k}x^k(1+x^{k+1})...(1+x^n) x(1+x)(1+x2)....(1+xn)=kakxk(1+xk+1)...(1+xn)

注意左右两边的 x x x不能抄掉了。这样我们就将组合关系抽象成了一个代数式。注意,我们只需要求出 { a i } \{a_i\} {ai}中所有 ≤ n \le n n的项的值然后用总方案数减去即可。

注意,在生成函数的运算中形式非常重要。借助杨表的组合意义,我们知道 ( 1 + x k ) . . . ( 1 + x n ) = ∑ i ≤ n x i ( i − 1 ) 2 + i k ∏ j ≤ i 1 1 − x j (1+x^k)...(1+x^n)=\sum_{i\le n}x^{\frac{i(i-1)}{2}+ik}\prod_{j\le i}\frac{1}{1-x^j} (1+xk)...(1+xn)=inx2i(i1)+ikji1xj1,因此可以将右边的式子写成 ∑ k a k x k ∑ i ≤ n x i ( i − 1 ) 2 + i k ∏ j ≤ i 1 1 − x j \sum_ka_kx^k\sum_{i\le n}x^{\frac{i(i-1)}{2}+ik}\prod_{j\le i}\frac{1}{1-x^j} kakxkinx2i(i1)+ikji1xj1。右边的封闭形式不是我们关注的重点,因为再不济也可以直接暴力卷积求出。我们重点关注 x x x的次数。

{ a i } \{a_i\} {ai}从何解出?注意,我们要求的不是 { a i } \{a_i\} {ai}的某一项,而是 A ( x ) = ∑ x k a k A(x)=\sum x^ka_k A(x)=xkak这个多项式。 x x x的次数是自由的,那么我们想到将 x x x的次数合并到一起,并且看到 x i k x^{ik} xik这一项,很容易让我们想到是将 x i x^i xi这个点值带入到 A ( x ) A(x) A(x)后的结果,基于这个想法,我们得到:

x ( 1 + x ) ( 1 + x 2 ) . . . ( 1 + x n ) = ∑ i ≤ n x i ( i + 1 ) 2 ∏ j ≤ i 1 1 − x j ∑ k a k x ( i + 1 ) k x(1+x)(1+x^2)...(1+x^n)=\sum_{i\le n}x^{\frac{i(i+1)}{2}}\prod_{j\le i}\frac{1}{1-x^j}\sum_ka_kx^{(i+1)k} x(1+x)(1+x2)...(1+xn)=inx2i(i+1)ji1xj1kakx(i+1)k

后面那个写成点值表示:

x ( 1 + x ) ( 1 + x 2 ) . . . ( 1 + x n ) = ∑ i ≤ n x i ( i + 1 ) 2 ∏ j ≤ i 1 1 − x j A ( x i + 1 ) x(1+x)(1+x^2)...(1+x^n)=\sum_{i\le n}x^{\frac{i(i+1)}{2}}\prod_{j\le i}\frac{1}{1-x^j}A(x^{i+1}) x(1+x)(1+x2)...(1+xn)=inx2i(i+1)ji1xj1A(xi+1)

这样整个式子只有 A ( x ) A(x) A(x)这一个变量了。将 A ( x ) A(x) A(x)单独提出来,我们得到:

A ( x ) = x ( 1 + x ) ( 1 + x 2 ) . . . ( 1 + x n ) − ∑ i ≥ 1 x i ( i + 1 ) 2 ∏ j ≤ i 1 1 − x j A ( x i + 1 ) A(x)=x(1+x)(1+x^2)...(1+x^n)-\sum_{i\ge 1}x^{\frac{i(i+1)}{2}}\prod_{j\le i}\frac{1}{1-x^j}A(x^{i+1}) A(x)=x(1+x)(1+x2)...(1+xn)i1x2i(i+1)ji1xj1A(xi+1)

好了,终于可以开始比较系数了!右边的式子看着很复杂,但是我们应该注意到左边式子的次数为 n n n,并且 系数是可以进行递推的,具体的,我们先处理出前 n 2 \frac{n}{2} 2n项,然后对于后 n 2 \frac{n}{2} 2n项,我们发现都可以通过前 n 2 \frac{n}{2} 2n的值暴力卷积算出,并且观察右边 x x x的次数发现最多只需要做 n \sqrt{n} n 次卷积,这样就做完了。唯一需要注意的是 j j j从大到小合并这个计算方式也挺妙的。

变量下标推错了一堆。还是太菜了。

复杂度 O ( n n ) O(n\sqrt{n}) O(nn )

生成函数 n b nb nb啊!!!

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define db double
using namespace std;
const int N=5e5+5;
int n,mod;
ll f[N],a[N],fac2[N],res;
void add(ll &x,ll y){if((x+=y)>=mod)x-=mod;}
void solve(int n){
    if(n==1)return;
    int m=n>>1;solve(m);
    for(int j=0;j<=n;j++)f[j]=0;
    for(int i=n;i;i--){
        if((ll)i*(i+1)/2<=n){
            for(int j=1;j<=m&&j*(i+1)<=n;j++)add(f[j*(i+1)],a[j]);
            for(int j=n;j>=i;j--)f[j]=f[j-i];
            for(int j=1;j<i;j++)f[j]=0;
            for(int j=i;j<=n;j++)add(f[j],f[j-i]);
        }
    }
    for(int j=m+1;j<=n;j++)add(a[j],mod-f[j]);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>mod,f[1]=a[1]=1;
    for(int i=1;i*(i+1)/2<=n;i++){
        for(int j=i;j<=n;j++)add(f[j],f[j-i]);
        for(int j=1;j+i*(i+1)/2<=n;j++)add(a[j+i*(i+1)/2],f[j]);
    }
    solve(n);
    fac2[0]=1;for(int i=1;i<=n;i++)fac2[i]=fac2[i-1]*2%mod;
    res=fac2[n];
    for(int i=1;i<=n;i++)add(res,mod-a[i]*fac2[n-i]%mod);
    cout<<(res+mod)%mod;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值