【XSY2166】Hope 分治 FFT

题目描述

  对于一个\(1\)到\(n\)的排列\(a_1,a_2,a_3,\ldots,a_n\),我们定义这个排列的\(P\)值和\(Q\)值:

  对于每个\(a_i\),如果存在一个最小的\(j\)使得\(i<j\)且\(a_i<a_j\),那么将\(a_i\)和\(a_j\)连一条无向边。于是就得到一幅图。计算这幅图每个联通块的大小,将它们相乘,得到\(P\)。记\(Q=P^k\)。

  对于\(1\)到\(n\)的所有排列,我们想知道它们的\(Q\)值之和。由于答案可能很大,请将答案对\(998244353\)取模。

  \(n,k\leq 100000\)

题解

  考虑从小到大插入这\(n\)个数。

  设\(f_i\)为所有\(1\)~\(i\)的排列的\(Q\)值之和。

  考虑\(i\)的位置,当\(i\)在第\(j\)个位置的时候,前面\(j\)个点是联通的,后面\(i-j\)个点与前面\(j\)个点不连通。
\[
\begin{align}
f_i&=\sum_{j=1}^i\binom{i-1}{j-1}(j-1)!j^kf_{i-j}\f_i&=\sum_{j=1}^i\frac{(i-1)!j^kf_{i-j}}{(i-j)!}\f_i&=(i-1)!\sum_{j=1}^ij^k\frac{f_{i-j}}{(i-j)!}
\end{align}
\]
  用分治FFT加速。

  时间复杂度:\(O(n\log k+n\log^2n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
    if(a>b)
        swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
    char str[100];
    sprintf(str,"%s.in",s);
    freopen(str,"r",stdin);
    sprintf(str,"%s.out",s);
    freopen(str,"w",stdout);
#endif
}
const ll p=998244353;
ll fp(ll a,ll b)
{
    ll s=1;
    while(b)
    {
        if(b&1)
            s=s*a%p;
        a=a*a%p;
        b>>=1;
    }
    return s;
}
namespace ntt
{
    const ll g=3;
    ll w1[270010];
    ll w2[270010];
    int rev[270010];
    int n;
    void init(int m)
    {
        n=1;
        while(n<m)
            n<<=1;
        int i;
        for(i=2;i<=n;i<<=1)
        {
            w1[i]=fp(g,(p-1)/i);
            w2[i]=fp(w1[i],p-2);
        }
        rev[0]=0;
        for(i=1;i<n;i++)
            rev[i]=(rev[i>>1]>>1)|(i&1?n>>1:0);
    }
    void ntt(ll *a,int t)
    {
        ll u,v,w,wn;
        int i,j,k;
        for(i=0;i<n;i++)
            if(rev[i]<i)
                swap(a[i],a[rev[i]]);
        for(i=2;i<=n;i<<=1)
        {
            wn=(t==1?w1[i]:w2[i]);
            for(j=0;j<n;j+=i)
            {
                w=1;
                for(k=j;k<j+i/2;k++)
                {
                    u=a[k];
                    v=a[k+i/2]*w%p;
                    a[k]=(u+v)%p;
                    a[k+i/2]=(u-v)%p;
                    w=w*wn%p;
                }
            }
        }
        if(t==-1)
        {
            ll inv=fp(n,p-2);
            for(i=0;i<n;i++)
                a[i]=a[i]*inv%p;
        }
    }
    ll x[270010];
    ll y[270010];
    void copy_clear(ll *a,ll *b,int m)
    {
        int i;
        for(i=0;i<m;i++)
            a[i]=b[i];
        for(i=m;i<n;i++)
            a[i]=0;
    }
    void copy(ll *a,ll *b,int m)
    {
        int i;
        for(i=0;i<m;i++)
            a[i]=b[i];
    }
    void inverse(ll *a,ll *b,int m)
    {
        if(m==1)
        {
            b[0]=fp(a[0],p-2);
            return;
        }
        inverse(a,b,m>>1);
        init(2*m);
        copy_clear(x,a,m);
        copy_clear(y,b,m>>1);
        ntt(x,1);
        ntt(y,1);
        int i;
        for(i=0;i<n;i++)
            x[i]=(2*y[i]%p-x[i]*y[i]%p*y[i]%p+p)%p;
        ntt(x,-1);
        copy(b,x,m);
    }
};
ll fac[300010];
ll ifac[300010];
ll inv[300010];
ll f[300010];
ll a[300010];
ll b[300010];
ll ex[300010];
int n,k;
void solve(int l,int r)
{
    if(l==r)
    {
        f[l]=f[l]*fac[l-1]%p;
        return;
    }
    int mid=(l+r)>>1;
    solve(l,mid);
    ntt::init(r-l+1);
    int i;
    for(i=l;i<=mid;i++)
        a[i-l]=f[i]*ifac[i];
    for(i=l;i<=r;i++)
        b[i-l]=ex[i-l];
    for(i=mid-l+1;i<ntt::n;i++)
        a[i]=0;
    for(i=r-l+1;i<ntt::n;i++)
        b[i]=0;
    ntt::ntt(a,1);
    ntt::ntt(b,1);
    for(i=0;i<ntt::n;i++)
        a[i]=a[i]*b[i]%p;
    ntt::ntt(a,-1);
    for(i=mid+1;i<=r;i++)
        f[i]+=a[i-l];
    solve(mid+1,r);
}
int main()
{
    open("xsy2166");
    scanf("%d%d",&n,&k);
    int i;
    fac[0]=fac[1]=ifac[0]=ifac[1]=inv[0]=inv[1]=1;
    for(i=2;i<=n;i++)
    {
        inv[i]=-(p/i)*inv[p%i]%p;
        fac[i]=fac[i-1]*i%p;
        ifac[i]=ifac[i-1]*inv[i]%p;
    }
    for(i=1;i<=n;i++)
    {
        ex[i]=fp(i,k);
        f[i]=ex[i];
    }
    solve(1,n);
    ll ans=(f[n]+p)%p;
    printf("%lld\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/ywwyww/p/8513236.html

时间: 2024-09-28 16:26:49

【XSY2166】Hope 分治 FFT的相关文章

2017 3 11 分治FFT

考试一道题的递推式为$$f[i]=\sum_{j=1}^{i} j^k \times (i-1)! \times \frac{f[i-j]}{(i-j)!}$$这显然是一个卷积的形式,但$f$需要由自己卷过来(我也不知到怎么说),以前只会生成函数的做法,但这题好像做不了(谁教教我怎么做),于是无奈的写了一发暴力,看题解发现是分治FFT.分治每层用$f[l]-f[mid]$与$a[1]-a[r-l]$做NTT.这样显然每个$f[l]-f[mid]$对$f[mid+1]-f[r]$的贡献都考虑到了.

【bzoj4836】[Lydsy2017年4月月赛]二元运算 分治+FFT

题目描述 定义二元运算 opt 满足 现在给定一个长为 n 的数列 a 和一个长为 m 的数列 b ,接下来有 q 次询问.每次询问给定一个数字 c 你需要求出有多少对 (i, j) 使得 a_i  opt b_j=c . 输入 第一行是一个整数 T (1≤T≤10) ,表示测试数据的组数. 对于每组测试数据: 第一行是三个整数 n,m,q (1≤n,m,q≤50000) . 第二行是 n 个整数,表示 a_1,a_2,?,a_n (0≤a_1,a_2,?,a_n≤50000) . 第三行是 m

HDU Shell Necklace CDQ分治+FFT

Shell Necklace Problem Description Perhaps the sea‘s definition of a shell is the pearl. However, in my view, a shell necklace with n beautiful shells contains the most sincere feeling for my best lover Arrietty, but even that is not enough. Suppose

看无可看 分治FFT+特征值方程

题面: 看无可看(see.pas/cpp/c) 题目描述 “What’s left to see when our eyes won’t open?” “若彼此瞑目在即,是否终亦看无可看?” ------来自网易云音乐<Golden Leaves-Passenger> 最后的一刻我看到了...... 一片昏暗? 我记起来了, 我看到,那里有一个集合S,集合S中有n个正整数a[i](1<=i<=n) 我看到,打破昏暗的密码: 记忆中的f是一个数列,对于i>1它满足f(i)=2*

【BZOJ3451】Tyvj1953 Normal 点分治+FFT+期望

[BZOJ3451]Tyvj1953 Normal Description 某天WJMZBMR学习了一个神奇的算法:树的点分治!这个算法的核心是这样的:消耗时间=0Solve(树 a) 消耗时间 += a 的 大小 如果 a 中 只有 1 个点  退出 否则在a中选一个点x,在a中删除点x 那么a变成了几个小一点的树,对每个小树递归调用Solve我们注意到的这个算法的时间复杂度跟选择的点x是密切相关的.如果x是树的重心,那么时间复杂度就是O(nlogn)但是由于WJMZBMR比较傻逼,他决定随机

[BZOJ4555][TJOI2016&amp;HEOI2016]求和(分治FFT)

解法一:容易得到递推式,可以用CDQ分治+FFT 代码用时:1h 比较顺利,没有低级错误. 实现比较简单,11348ms #include<cstdio> #include<algorithm> #define rep(i,l,r) for (int i=l; i<=r; i++) typedef long long ll; using namespace std; const int N=(1<<18)+100,P=998244353,g=3; int n,re

【XSY2744】信仰圣光 分治FFT 多项式exp 容斥原理

题目描述 有一个\(n\)个元素的置换,你要选择\(k\)个元素,问有多少种方案满足:对于每个轮换,你都选择了其中的一个元素. 对\(998244353\)取模. \(k\leq n\leq 152501\) 题解 吐槽 为什么一道FFT题要把\(n\)设为\(150000\)? 解法一 先把轮换拆出来. 直接DP. 设\(f_{i,j}\)为前\(i\)个轮换选择了\(j\)个元素,且每个轮换都选择了至少一个元素的方案数. \[ f_{i,j}=\sum_{k=1}^{a_i}f_{i-1,j

hdu5730 Shell Necklace 【分治fft】

题目 简述: 有一段长度为n的贝壳,将其划分为若干段,给出划分为每种长度的方案数,问有多少种划分方案 题解 设\(f[i]\)表示长度为\(i\)时的方案数 不难得dp方程: \[f[i] = \sum\limits_{j=0}^{i} a[j] * f[i - j]\] 考虑转移 直接转移是\(O(n^2)\)的 如何优化? 容易发现这个转移方程非常特别,是一个卷积的形式 考虑fft 分治fft 分治fft解决的就是这样一个转移方程的快速计算的问题 \[f[i] = \sum\limits_{

bzoj千题计划309:bzoj4332: JSOI2012 分零食(分治FFT)

https://www.lydsy.com/JudgeOnline/problem.php?id=4332 因为如果一位小朋友得不到糖果,那么在她身后的小朋友们也都得不到糖果. 所以设g[i][j] 表示前i位小朋友,分到j个糖果,且前i位小朋友都分到糖果的方案数 令F(x) 表示分到x个糖果的欢乐程度 ∴g[i][j] = ∑ g[i-1][j-k]*F(k) 记g[i]=g[i-1]*F,则 g[i]=F ^ i 但是要求的是 Σ g[i][m] 记f[n]=Σ g[i]  i∈[1,n]