题目链接

以下把值域(题面里的\(lim\))记做\(m\)。

考虑求\(k\)的答案。考虑每个位置对答案的贡献,枚举位置\(i\),再枚举\(a[i]\)的值\(x\)。设:
\[
F(k)=\sum_{i=k+1}^{n}m^{i-k-1}m^{n-i}{i-1\choose k}\sum_{x=1}^{m}(x-1)^k
\]
如果对着式子打一个\(O(n^2m)\)的暴力,就会发现这个\(F(k)\)并不是答案,它比答案大。它其实是\(i\)前面至少有\(k\)个数的方案数。而一个序列,如果\(i\)前面有超过\(k\)个\(\leq x\)的数,比方说有\(j\)个,那这个序列就会被计算\(j\choose k\)次。形式化地说,如果设\(i\)前面恰好有\(k\)个\(\leq x\)数的方案数为\(G(k)\),则:
\[
F(k)=\sum_{i=k}^{n}{i\choose k}G(i)
\]
而这个\(G(k)\)的后缀和,就是我们要求的答案。

根据二项式反演,可知:
\[
G(k)=\sum_{i=k}^{n}(-1)^{i-k}{i\choose k}F(i)
\]
如果知道了\(F[1\dots n]\),就可以用NTT快速求出\(G[1\dots n]\),进而求出答案。这部分我们稍后再说,先看如何求\(F\)。

设\(S(m,k)=\sum_{i=0}^{m-1}i^k\)。则:
\[
\begin{align}
F(k)=&m^{n-k-1}S(m,k)\sum_{i=k+1}^{n}{i-1\choose k}\\
=&m^{n-k-1}S(m,k)\sum_{i=k}^{n-1}{i\choose k}\\
=&m^{n-k-1}S(m,k){n\choose k+1}
\end{align}
\]
最后一步相当于求杨辉三角上连续若干行,每行的第\(k\)个数的和(画出来是一条斜向左下的斜线)。可以从上到下把每两个数合并为下一行的第\(k+1\)个数。因此\(\sum_{i=k}^{n-1}{i\choose k}={n\choose k+1}\)。

现在的难点是求出对所有\(k\in[1,n]\),求出\(S(m,k)\)。构造\(S(m,k)\)的指数生成函数\(H(x)=\sum_{i=0}^{\infty}S(m,i)\frac{x^i}{i!}\),则:
\[
\begin{align}
H(x)&=\sum_{i=0}^{\infty}\frac{x^i}{i!}\sum_{j=0}^{m-1}j^i\\
&=\sum_{j=0}^{m-1}\sum_{i=0}^{\infty}\frac{x^ij^i}{i!}
\end{align}
\]
根据小学二年级学过的泰勒展开,我们知道,\(e^x=\sum_{i=0}^{\infty}\frac{x^i}{i!}\)。把这里的\(x\)替换为\((jx)\),则:
\[
\begin{align}
H(x)&=\sum_{j=0}^{m-1}e^{jx}\\
&=\frac{e^{mx}-1}{e^x-1}
\end{align}
\]
注:这一步变换是等比数列求和,即把式子整体乘以\(e^x\)再用新式子减去原式。

再次感谢泰勒展开,我们知道分子和分母其实分别是两个无限长的数列。我们要求是\(H(x)\)的前\(n\)项的值。这个值显然只和两个数列的前\(n\)项有关,因此取出分母的前\(n\)项,做多项式求逆,再用NTT把两个数列乘起来即可。分母的常数项是\(0\)不好处理,但注意到分子的常数项也是\(0\),我们只要把分子分母同时除以\(x\)就行,即两个数列分别左移一位就行。

这样,我们就求出了\(F[0\dots n-1]\),回顾开头部分的讨论,我们要求出:\(G(k)=\sum_{i=k}^{n-1}(-1)^{i-k}{i\choose k}F(i)\quad k\in[0,n-1]\)。

把组合数拆开,得到:
\[
G(k)=\sum_{i=k}^{n-1}(-1)^{i-k}\frac{i!}{k!(i-k)!}F(i)
\]
先把\(k!\)拎出来。然后设\(f_i=i!F(i),g_i=(-1)^i\frac{1}{i!}\),则:
\[
G(k)=k!\sum_{i=k}^{n-1}f_ig_{i-k}
\]
我们熟悉的卷积都是\(c_k=\sum_{i=0}^{k}a_ib_{k-i}\),而这里的形式是\(c_k=\sum_{i=k}^{n}f_ig_{i-k}\),考虑如何转化。注:这里为方便表述,我们令\(n=n-1\)。

仔细考虑卷积的本质,我们的目标是要让相乘的两个数下标之和为定值,而我们能做的事就是尝试翻转一个数列。不妨尝试翻转\(f\),即令\(f_i=f_{n-i}\)。

此时\(c_k=\sum_{i=k}^{n}f_{n-i}g_{i-k}\)。

把\(i\)换成\(i-k\),则\(c_k=\sum_{i=0}^{n-k}f_{n-k-i}g_{i}\)。发现\(f\)和\(g\)的下标和为\(n-k\),刚好是一个定值。此时如果把\(f\)和\(g\)卷起来,那么结果的第\(n-k\)项就是\(c_k\)。再乘上\(k!\)就是\(G(k)\)了。

求出\(G\)后,做后缀和,得到答案。

时间复杂度\(O(n\log n)\),共做了一次多项式求逆,两次多项式乘法。

参考代码:

//problem:nflsoj550
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fst first
#define scd second

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

/*  ------  by:duyi  ------  */ // dysyn1314
const int MAXN=114514,MOD=998244353;
inline int mod1(int x){return x<MOD?x:x-MOD;}
inline int mod2(int x){return x<0?x+MOD:x;}
inline void add(int &x,int y){x=mod1(x+y);}
inline void sub(int &x,int y){x=mod2(x-y);}
inline int pow_mod(int x,int i){int y=1;while(i){if(i&1)y=(ll)y*x%MOD;x=(ll)x*x%MOD;i>>=1;}return y;}
int n,m,fac[MAXN+5],invf[MAXN+5];
void init(){
    fac[0]=1;
    for(int i=1;i<=MAXN;++i)fac[i]=(ll)fac[i-1]*i%MOD;
    invf[MAXN]=pow_mod(fac[MAXN],MOD-2);
    for(int i=MAXN-1;i>=0;--i)invf[i]=(ll)invf[i+1]*(i+1)%MOD;
}
inline int comb(int n,int k){
    if(n<k)return 0;
    return (ll)fac[n]*invf[k]%MOD*invf[n-k]%MOD;
}
int f[MAXN*4+5],g[MAXN*4+5],h[MAXN*4+5],rev[MAXN*4+5];
void NTT(int *a,int n,int flag){
    for(int i=0;i<n;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
    for(int i=1;i<n;i<<=1){
        int T=pow_mod(3,(MOD-1)/(i<<1));
        if(flag==-1) T=pow_mod(T,MOD-2);
        for(int j=0;j<n;j+=(i<<1)){
            for(int k=0,t=1;k<i;++k,t=(ll)t*T%MOD){
                int Nx=a[j+k],Ny=(ll)a[i+j+k]*t%MOD;
                a[j+k]=mod1(Nx+Ny);
                a[i+j+k]=mod2(Nx-Ny);
            }
        }
    }
    if(flag==-1){
        int invn=pow_mod(n,MOD-2);
        for(int i=0;i<n;++i)a[i]=(ll)a[i]*invn%MOD;
    }
}
void mul(int *a,int *b,int n,int m){
    // b <- a*b
    static int f[MAXN*4+5],g[MAXN*4+5];
    for(int i=0;i<n;++i)f[i]=a[i];
    for(int i=0;i<m;++i)g[i]=b[i];
    int lim=1,ct=0;
    while(lim<=n+m)lim<<=1,ct++;
    for(int i=n;i<lim;++i)f[i]=0;
    for(int i=m;i<lim;++i)g[i]=0;
    for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(ct-1));
    NTT(f,lim,1);
    NTT(g,lim,1);
    for(int i=0;i<lim;++i)f[i]=(ll)f[i]*g[i]%MOD;
    NTT(f,lim,-1);
    for(int i=0;i<n;++i)b[i]=f[i];
}
void _mul(int *a,int *b,int n,int m){
    // b <- 2b-a*b^2
    static int f[MAXN*4+5],g[MAXN*4+5];
    for(int i=0;i<n;++i)f[i]=a[i];
    for(int i=0;i<m;++i)g[i]=b[i];
    int lim=1,ct=0;
    while(lim<=(n*2))lim<<=1,ct++;
    for(int i=n;i<lim;++i)f[i]=0;
    for(int i=m;i<lim;++i)g[i]=0;
    for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(ct-1));
    NTT(f,lim,1);
    NTT(g,lim,1);
    for(int i=0;i<lim;++i)f[i]=mod2(mod1(g[i]*2)-(ll)f[i]*g[i]%MOD*g[i]%MOD);
    NTT(f,lim,-1);
    for(int i=0;i<n;++i)b[i]=f[i];
}
void get_inv(int *a,int n,int *res){
    if(n==1){
        res[0]=pow_mod(a[0],MOD-2);
        return;
    }
    int m=(n+1)>>1;
    get_inv(a,m,res);
    _mul(a,res,n,m);
}
int main() {
    init();
    scanf("%d%d",&n,&m);m%=MOD;
    for(int i=0,pw=1;i<n;++i){
        pw=(ll)pw*m%MOD;
        f[i]=(ll)pw*invf[i+1]%MOD;
        g[i]=invf[i+1];
    }
    get_inv(g,n,h);//h=g^{-1}
    mul(f,h,n,n);//h=f*h
    for(int i=0;i<n;++i)h[i]=(ll)h[i]*fac[i]%MOD;
    //for(int i=0;i<n;++i)cout<<h[i]<<" ";cout<<endl;
    memset(f,0,sizeof(f));
    memset(g,0,sizeof(g));
    for(int k=0;k<n;++k)f[k]=(ll)pow_mod(m,n-k-1)*h[k]%MOD*comb(n,k+1)%MOD;//f[k]至少选k个的贡献
    //f[k] -> g[k](恰好选k个的贡献)
    for(int i=0;i<n;++i)f[i]=(ll)f[i]*fac[i]%MOD;
    for(int i=0;i<n;++i)h[i]=f[n-1-i];
    for(int i=0;i<n;++i)f[i]=h[i];
    for(int i=0;i<n;++i)if(i&1)g[i]=mod2(-invf[i]);else g[i]=invf[i];
    mul(f,g,n,n);
    for(int i=0;i<n;++i)h[i]=g[n-1-i];
    for(int i=0;i<n;++i)g[i]=(ll)h[i]*invf[i]%MOD;g[n]=0;
    for(int i=n-1;i>=1;--i)add(g[i],g[i+1]);
    for(int i=1;i<=n;++i)printf("%d ",g[i]);puts("");
    return 0;
}

题解 nflsoj550 【六校联合训练 省选 #9】序列的更多相关文章

  1. 题解 nflsoj553 【六校联合训练 省选 #10】飞

    题目链接 我们称"简要题意"给出的三个要求分别为"条件1","条件2","条件3". 条件3长得比较丑,考虑转化一下.把 ...

  2. NFLSOJ 1072 - 【2021 六校联合训练 NOIP #1】异或(FWT+插值)

    题面传送门 一道非常不错的 FWT+插值的题 %%%%%%%%%%%% 还是那句话,反正非六校的看不到题对吧((( 方便起见在下文中设 \(n=2^d\). 首先很明显的一点是这题涉及两个维度:异或和 ...

  3. NFLSOJ 1060 - 【2021 六校联合训练 NOI #40】白玉楼今天的饭(子集 ln)

    由于 NFLSOJ 题面上啥也没有就把题意贴这儿了( 没事儿,反正是上赛季的题,你们非六校学生看了就看了,况且看了你们也没地方交就是了 题意: 给你一张 \(n\) 个点 \(m\) 条边的图 \(G ...

  4. 题解 nflsoj489 【六校联合训练 CSP #15】小D与随机

    题目链接 考虑枚举好点的集合.此时要考虑的问题是如何填入\(1\sim n\)这些数使得恰好我们枚举到的这些点是好点,即:求出有多少种合法的填数方案. \(1\)号点一定是好点.那么除\(1\)号点外 ...

  5. HDU 5358(2015多校联合训练赛第六场1006) First One (区间合并+常数优化)

    pid=5358">HDU 5358 题意: 求∑​i=1​n​​∑​j=i​n​​(⌊log​2​​S(i,j)⌋+1)∗(i+j). 思路: S(i,j) < 10^10 & ...

  6. 2017多校联合训练2—HDU6054--Is Derek lying?(思维题)

    Is Derek lying? Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)T ...

  7. hdu 4649 Professor Tian 多校联合训练的题

    这题起初没读懂题意,悲剧啊,然后看了题解写完就AC了 题意是给一个N,然后给N+1个整数 接着给N个操作符(只有三种操作  即  或 ,与 ,和异或 |   &  ^ )这样依次把操作符插入整 ...

  8. HDU 4643 GSM 暑期多校联合训练第五场 1001

    点击打开链接 我就不说官方题解有多坑了 V图那么高端的玩意儿 被精度坑粗翔了 AC前 AC后 简直不敢相信 只能怪自己没注意题目For the distance d1 and d2, if fabs( ...

  9. 2017ACM暑期多校联合训练 - Team 8 1011 HDU 6143 Killer Names (容斥+排列组合,dp+整数快速幂)

    题目链接 Problem Description Galen Marek, codenamed Starkiller, was a male Human apprentice of the Sith ...

随机推荐

  1. techiediaries网站的Laravel 6系列教程

    Laravel 6 Tutorial & New Features - Build a CRM [PART 1] Laravel 6 REST API CRUD Tutorial - Buil ...

  2. Edge Beta Android版更新已启用新图标

    导读 微软Edge Beta Android版更新已启用新图标设计 IT之家消息 适用于Android的Microsoft Edge Beta已于近日获得更新,最显著的特征就是使用了新图标设计.该图标 ...

  3. vue修改当前页样式不影响公共样式的方法

    在项目开发中需要对一些标签进行样式修改但是每次修改之后其他页面的样式也会跟着改变, 在网上找了很多方法都不好使后来大神告诉我一种方法很好用分享给大家. 1:首先在template标签下的第一个div中 ...

  4. linux kali 的ifconfig命令

    ifconfig命令 1.ifconfig执行页面 root@localhost:/home/zys# ifconfig lo: flags=73<UP,LOOPBACK,RUNNING> ...

  5. python知识点总结以及15道题的解析

    先看知识点总结 一.序列操作符x in s 如果x是列表s的元素,返回True,否则Falses + t 连接两个序列s和ts*n或者n*s 将序列s复制n次s[i] 返回s中第i元素s[i:j]或s ...

  6. Codeforces #617 (Div. 3) C. Yet Another Walking Robot

    There is a robot on a coordinate plane. Initially, the robot is located at the point (0,0)(0,0) . It ...

  7. c++读取注册表的实例

    // CRegisterTest.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #in ...

  8. Nodejs 开发 随手记

    console.log(Object.prototype.toString.call(Now.toString())); //类型判断

  9. C语言中的结构体是怎么定义的_怎么使用?

    结构体的定义 // 定义结构体st struct st{ int a; // 成员a int b; // 成员b }; #include <stdio.h> struct st{ int ...

  10. 从系统引导菜单禁用Hyper-V

    1, 从当前引导运行的系统复制一个新引导菜单项: bcdedit /copy {current} /d "Win10 NO_HV" 这时候cmd会输出新引导项的 guid, 复制出 ...