【学习笔记】[ARC145F] Modulo Sum of Increasing Sequences

单位根反演好题。

提示:是照搬的 这篇题解 的做法,只是加了一点小小的解释。

首先,做等价变换:给第 i i i个位置加上 i − 1 i-1 i1,问题转化为了求单调递增序列,即从 [ 0 , N + M − 1 ] [0,N+M-1] [0,N+M1]中选 N N N不同的数,使得这些数模 mod ⁡ \operatorname{mod} mod的值为 k k k

这事实上是一个二元 G F GF GF的问题。先不考虑选 N N N个数的限制,记 n = N + M n=N+M n=N+M,写出答案的生成函数形式: ∏ i = 0 n − 1 ( 1 + x i ) \prod_{i=0}^{n-1}(1+x^i) i=0n1(1+xi)

k = mod ⁡ k=\operatorname{mod} k=mod,那么我们要求所有 i k + r ik+r ik+r项的系数和,这通常可以用单位根反演来解决。

对于任意多项式 f ( x ) f(x) f(x),我们有:

∑ k ∣ i [ x i ] f ( x ) = ∑ i = 0 n − 1 [ x i ] f ( x ) 1 k ∑ j = 0 k − 1 w k i j = 1 k ∑ i = 0 n − 1 a i ∑ j = 0 k − 1 ( w k j ) i = 1 k ∑ j = 0 k − 1 ∑ i = 0 n − 1 a i ( w k j ) i = 1 k ∑ j = 0 k − 1 f ( w k j ) \begin{aligned}\sum_{k|i}[x^i]f(x)&=\sum_{i=0}^{n-1}[x^i]f(x)\frac{1}{k}\sum_{j=0}^{k-1}w_k^{ij}\\&=\frac{1}{k}\sum_{i=0}^{n-1}a_i\sum_{j=0}^{k-1}(w_k^j)^i\\&=\frac{1}{k}\sum_{j=0}^{k-1}\sum_{i=0}^{n-1}a_i(w_k^j)^i\\&=\frac{1}{k}\sum_{j=0}^{k-1}f(w_k^j)\end{aligned} ki[xi]f(x)=i=0n1[xi]f(x)k1j=0k1wkij=k1i=0n1aij=0k1(wkj)i=k1j=0k1i=0n1ai(wkj)i=k1j=0k1f(wkj)

类似的: ∑ [ x i k + r ] f ( x ) = 1 k ∑ j = 0 k − 1 w k − j r f ( w k j ) \sum[x^{ik+r}]f(x)=\frac{1}{k}\sum_{j=0}^{k-1}w_k^{-jr}f(w_k^j) [xik+r]f(x)=k1j=0k1wkjrf(wkj)

注意这里的 k k k不是 2 2 2的次幂,因此不能直接计算。要想办法把单位根搞掉。

注意到 x n − 1 = ∏ i = 0 n − 1 ( x − w n i ) x^n-1=\prod_{i=0}^{n-1}(x-w_n^i) xn1=i=0n1(xwni) x = − 1 x=-1 x=1,那么 ∏ i = 0 n − 1 ( w n i + 1 ) = 1 − ( − 1 ) n \prod_{i=0}^{n-1}(w_n^i+1)=1-(-1)^n i=0n1(wni+1)=1(1)n

考虑 k ∣ n k|n kn的情况。 d = gcd ⁡ ( j , k ) d=\gcd(j,k) d=gcd(j,k),此时 gcd ⁡ ( j d , k d ) = 1 \gcd(\frac{j}{d},\frac{k}{d})=1 gcd(dj,dk)=1

f ( w k j ) = ∏ i = 0 n − 1 ( 1 + w k i j ) = ∏ i = 0 k d − 1 ( 1 + w k d i ) n d k = ( 1 − ( − 1 ) k d ) n d k \begin{aligned}f(w_k^j)&=\prod_{i=0}^{n-1}(1+w_k^{ij})\\&=\prod_{i=0}^{\frac{k}{d}-1}(1+w_{\frac{k}{d}}^{i})^{\frac{nd}{k}}\\&=(1-(-1)^{\frac{k}{d}})^{\frac{nd}{k}}\end{aligned} f(wkj)=i=0n1(1+wkij)=i=0dk1(1+wdki)knd=(1(1)dk)knd

带入原式即: 1 k ∑ j = 0 k − 1 w k − j r ( 1 − ( − 1 ) k d ) n d k \frac{1}{k}\sum_{j=0}^{k-1}w_k^{-jr}(1-(-1)^{\frac{k}{d}})^{\frac{nd}{k}} k1j=0k1wkjr(1(1)dk)knd,现在考虑怎么把前面的单位根搞掉。看到 gcd ⁡ \gcd gcd考虑枚举 d d d,即:

L H S = 1 k ∑ d ∣ k ( 1 − ( − 1 ) k d ) n d k ∑ gcd ⁡ ( j , k ) = d w k − j r LHS=\frac{1}{k}\sum_{d|k}(1-(-1)^{\frac{k}{d}})^{\frac{nd}{k}}\sum_{\gcd(j,k)=d}w_k^{-jr} LHS=k1dk(1(1)dk)kndgcd(j,k)=dwkjr

后面那一坨看起来就很好化简:

∑ gcd ⁡ ( j , k ) = d w k − j r = ∑ i = 1 k d w k − i d r [ gcd ⁡ ( i , k d ) = 1 ] = ∑ i = 1 k d w k − i d r ∑ j ∣ gcd ⁡ ( i , k d ) μ ( j ) = ∑ j ∣ k d μ ( j ) ∑ i = 1 k d j w k − i j d r = ∑ j ∣ k d μ ( j ) ∑ i = 1 k d j w k d j − r i = ∑ j ∣ k d μ ( j ) [ k d j ∣ r ] \begin{aligned}\sum_{\gcd(j,k)=d}w_k^{-jr}&=\sum_{i=1}^{\frac{k}{d}}w_k^{-idr}[\gcd(i,\frac{k}{d})=1]\\&=\sum_{i=1}^{\frac{k}{d}}w_k^{-idr}\sum_{j|\gcd(i,\frac{k}{d})}\mu(j)\\&=\sum_{j|\frac{k}{d}}\mu(j)\sum_{i=1}^{\frac{k}{dj}}w_k^{-ijdr}\\&=\sum_{j|\frac{k}{d}}\mu(j)\sum_{i=1}^{\frac{k}{dj}}w_\frac{k}{dj}^{-ri}\\&=\sum_{j|\frac{k}{d}}\mu(j)[\frac{k}{dj}|r]\end{aligned} gcd(j,k)=dwkjr=i=1dkwkidr[gcd(i,dk)=1]=i=1dkwkidrjgcd(i,dk)μ(j)=jdkμ(j)i=1djkwkijdr=jdkμ(j)i=1djkwdjkri=jdkμ(j)[djkr]

最后一步是 逆用单位根反演 。因此最终答案为:

L H S = 1 k ∑ d ∣ k ( 1 − ( − 1 ) k d ) n d k g ( d , k ) \begin{aligned}LHS=\frac{1}{k}\sum_{d|k}(1-(-1)^{\frac{k}{d}})^{\frac{nd}{k}}g(d,k)\end{aligned} LHS=k1dk(1(1)dk)kndg(d,k)

其中 g ( d , k ) g(d,k) g(d,k)是固定的系数。现在考虑二元 G F GF GF,即 [ x i k + r y n ] ∏ i = 0 n − 1 ( 1 + x i y ) [x^{ik+r}y^n]\prod_{i=0}^{n-1}(1+x^iy) [xik+ryn]i=0n1(1+xiy)。前面的推导都一模一样(把 y y y当成参量来处理),不难验证答案为:

L H S = 1 k ∑ d ∣ k ( 1 − ( − y ) k d ) n d k g ( d , k ) LHS=\frac{1}{k}\sum_{d|k}(1-(-y)^{\frac{k}{d}})^{\frac{nd}{k}}g(d,k) LHS=k1dk(1(y)dk)kndg(d,k)

对于 n ∤ k n\nmid k nk的情况,可以将剩余的不足 k k k项暴力背包,即设 f i , j f_{i,j} fi,j表示选了 i i i个数,并且模 k k k等于 j j j的方案数。可以在 O ( k 3 ) O(k^3) O(k3)的时间内处理出来,最后合并答案也是 O ( k 3 ) O(k^3) O(k3)的。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fi first
#define se second
using namespace std;
const int mod=998244353;
const int N=505;
const int M=2e6+5;
int n,m,p,n2,mx;
ll now[N][N];
ll fac[M],inv[M],f[N],g[N][N],res[N];
ll fpow(ll x,ll y=mod-2){
    ll z(1);
    for(;y;y>>=1){
        if(y&1)z=z*x%mod;
        x=x*x%mod;
    }return z;
}
void init(int n){
    fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
    inv[n]=fpow(fac[n]);for(int i=n;i>=1;i--)inv[i-1]=inv[i]*i%mod;
}
int mu[N],vs[N];
int pr[N],cnt;
void init2(int n){
    mu[1]=1;
    for(int i=2;i<=n;i++){
        if(vs[i]==0)pr[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&pr[j]<=n/i;j++){
            vs[i*pr[j]]=1;
            if(i%pr[j]==0){
                mu[i*pr[j]]=0;
                break;
            }mu[i*pr[j]]=-mu[i];
        }
    }
}
ll binom(int x,int y){
    if(x<0||y<0||x<y)return 0;
    return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
void add(ll &x,ll y){
    x=(x+y)%mod;
}
vector<int>v;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m>>p,n2=n+m;
    init(n2),init2(p);
    mx=p*(n2/p);
    for(int i=1;i<=p;i++){
        if(p%i==0)v.pb(i);
    }
    for(int k=0;k<p;k++){
        for(auto d:v){
            for(auto j:v){
                if(p/d%j==0&&k%(p/d/j)==0){
                    add(g[k][d],mu[j]*p/d/j);
                }
            }
        }
    }
    now[0][0]=1;
    for(int i=mx;i<n2;i++){
        int s=i%p;
        for(int j=min(n2-mx,n);j>=1;j--){
            for(int k=0;k<p;k++){
                add(now[j][k],now[j-1][(k-s+p)%p]);
            }
        }
    }
    for(int i=0;i<=min(n,n2-mx);i++){
        memset(f,0,sizeof f);
        for(auto d:v){
            if((n-i)%(p/d))continue;
            int t=(n-i)/(p/d);
            for(int k=0;k<p;k++){
                if(p/d%2==1||t%2==0){
                    add(f[k],binom(d*mx/p,t)*g[k][d]);
                }
                else{
                    add(f[k],-binom(d*mx/p,t)*g[k][d]);
                }
            }
        }
        for(int j=0;j<p;j++){
            for(int k=0;k<p;k++){
                add(res[(j+k)%p],f[j]*now[i][k]);
            }
        }
    }for(int i=0;i<p;i++){
        res[i]=res[i]*fpow(p)%mod;
    }int tmp=(ll)n*(n-1)/2%p;
    for(int i=0;i<p;i++){
        cout<<(res[(i+tmp)%p]+mod)%mod<<" ";
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值