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

运算真是太强大辣!!!

这题的切入点非常有意思。记 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]); } assert(a[1]==1); assert(a[0]==0); solve(n); assert(a[1]==1); 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; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529960.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(11)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示