ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[UOJ50] 链式反应

2021-08-24 22:02:10  阅读:174  来源: 互联网

标签:UOJ50 链式反应 ll mid il frac aligned sum


题面

高质量好题。

写的时候脑子一片混乱,内容狗屁不通

算法一

首先根据题面,可以设 \(f_{i}\) 表示序列长度为 \(i\) 的时候的方案数,其中 \(f_{1}=1\) 。

那么有 \(f_{i}=\sum_{j=1}\sum_{k=1}[s_{i-j-k-1}=1]f_{j}f_{k}\binom{i-1}{i-j-k-1}\binom{j+k}{j}\)

两个组合数分别是选出 \(i-j-k-1\) 个点被删除和给两个剩下来的组重标号。

但是注意到这东西实际上会算重,因为 \((j,k)\) 和 \((k,j)\) 这个方案被计算了两次,而实际上它们是同一种方案,所以答案还要除以 \(2\) 。

虽然 \((j,j)\) 这个二元组只被算了一次,但是可以发现后面重标号部分使得这个地方被算了两次,刚好抵消了。所以直接除以 \(2\) 是没问题的。

于是我们就得到了一个 \(O(n^3)\) 的做法。

算法二

对上面的式子做一个变形,可以得到 :

\[\begin{aligned} \frac{2f_{i}}{(i-1)!}=\sum_{j=1}\sum_{k=1}[s_{i-j-k-1}=1]\frac{f_{j}}{j!}\frac{f_{k}}{k!}\frac{1}{(i-j-k-1)!} \end{aligned} \]

如果我们令 \(g_{t}=\sum_{i=1}\dfrac{f_{i}}{i!}\dfrac{f_{t-i}}{(t-i)!}\)

那么有

\[\begin{aligned} \frac{2f_{i}}{(i-1)!}=\sum^{i-1}_{t=0}[s_{i-t-1}=1]g_{t}\frac{1}{(i-t-1)!} \end{aligned} \]

就目前为止,已经可以做到 \(O(n^2)\) 了。

算法三

如果我们令 \(S_{i}=[s_{i}=1]\frac{1}{i!}\),那么有

\[\begin{aligned} \frac{2f_{i}}{(i-1)!}&=\sum^{i-1}_{t=0}S_{i-t-1}g_{t}\\ g_{t}&=\sum^{t}_{i=0}f_{i}f_{t-i} \end{aligned} \]

可以发现均为卷积的形式,这种半在线的卷积考虑用 CDQ 来优化。

为了方便,这里定义 \(F(x)_{l,r}\) 表示\(\sum^{r}_{i=l}f_{i}x^i\)

先看 $ F(x)$ 的部分,假设我们已经解决到了区间 \([l,r]\) 的部分,其中 \([l,mid]\) 已经解决,要计算 \([mid+1,r]\) 部分的答案:

\[\begin{aligned} S(x)G(x)_{l,r}&=S(x)G(x)_{l,mid}+S(x)G(x)_{mid+1,r}\\ \end{aligned} \]

看上去我们需要\([0,n-1]\) 的 \(S(x)\) ,但是实际上我们真正需要的部分是 \([mid+1,r]\) ,有很多地方是浪费的,尝试标一下上下界。

\[\begin{aligned} S(x)G(x)_{l,r}&=S(x)_{1,r-l}G(x)_{l,mid}+S(x)_{}G(x)_{mid+1,r}\\ \end{aligned} \]

左边部分可以 \(O(L\log L)\) 解决,右边部分是一个子问题,递归解决。

同样的,我们来看 \(G(x)\) 的部分:

\[\begin{aligned} F(x)F(x)_{l,r}&=F(x)F(x)_{l,mid}+F(x)F(x)_{mid+1,r}\\ &=F(x)_{1,r-l}F(x)_{l,mid}+F(x)F(x)_{mid+1,r}\\ \end{aligned} \]

这里似乎出现了问题, \(r-l\) 有可能比 \(mid\) 大,答案还没有计算出来。

可以发现此时的 \(l\) 一定等于 \(1\)。 (不难证明,自己可以推推看)

\[\begin{aligned} F(x)_{1,r-l}F(x)_{1,mid}&=F(x)_{1,mid}F(x)_{1,mid}+F(x)_{mid+1,r-1}F(x)_{1,mid}\\ \end{aligned} \]

前面一部分已经可以计算了,后面一部分把它丢到下一次递归里再计算。

也就是在 \(solve(mid+1,r)\) 的时候还需要计算 \(F(x)_{1,mid}F(x)_{mid+1,r-1}\)

这个东西就是在正常情况中 \(F(x)_{1,l-1}F(x)_{l,r-1}\) 。

继续把这个东西拆成\(F(x)_{1,l-1}F(x)_{l,mid}+F(x)_{1,l-1}F(x)_{mid+1,r-1}\)

左边这部分就是直接让 \(l\) 前面的系数乘二,右边那部分和\(F(x)_{1,mid}F(x)_{mid+1,r-1}\) 是等价的(因为多出来的部分不会被统计)。

这样子之后恰好就是每一个部分 \(l\) 前面的系数乘二。

Code
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ri register int
#define ll long long
#define ui unsigned int
il ll read(){
    bool f=true;ll x=0;
    register char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=false;ch=getchar();}
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    if(f) return x;
    return ~(--x);
}
il void write(const ll &x){if(x>9) write(x/10);putchar(x%10+'0');}
il void print(const ll &x) {x<0?putchar('-'),write(~(x-1)):write(x);putchar('\n');}
il ll max(const ll &a,const ll &b){return a>b?a:b;}
il ll min(const ll &a,const ll &b){return a<b?a:b;}
namespace Poly{
    const ll mod=998244353,inv2=(mod+1)/2;
    ll ksm(ll d,ll t){
        ll res=1;
        for(;t;t>>=1){
            if(t&1) res=res*d%mod;
            d=d*d%mod;
        }
        return res;
    }
    il ll dec(ll x,ll y) {return x>=y?x-y:x-y+mod;}
    il ll add(ll x,ll y) {return x+y<mod?x+y:x+y-mod;}
    const int N=262144*2;
    vector<ll> w[23];
    ll fac[N],ifac[N],inv[N];
    il ll C(ll x,ll y){
        if(x<y) return 0;
        return fac[x]*ifac[y]%mod*ifac[x-y]%mod;
    }
    void init(){
        int n=N-1;
        fac[0]=1;
        for(ri i=1;i<=n;++i) fac[i]=i*fac[i-1]%mod;
        ifac[n]=ksm(fac[n],mod-2);
        for(ri i=n-1;~i;--i) ifac[i]=ifac[i+1]*(i+1)%mod;
        for(ri i=n;i;--i) inv[i]=ifac[i]*fac[i-1]%mod;
        inv[0]=1;
        int d=log(N)/log(2)+0.5;
        for(ri i=1;i<=d;++i){
            w[i].resize(1<<i);
            w[i][0]=1,w[i][1]=ksm(3,(mod-1)>>i);
            for(ri j=2;j<(1<<i);++j) w[i][j]=w[i][j-1]*w[i][1]%mod;
        }
    }
    int r[N];
    void DFT(int limit,ll *a,int flag){
        for(ri i=1;i<limit;++i) if(r[i]<i) swap(a[i],a[r[i]]);
        for(ri l=1,t=1;l<limit;l<<=1,++t){
            for(ri i=0;i<limit;i+=l<<1){
                ll *W=&w[t][0];
                for(ri j=0;j<l;++j){
                    ll tmp=a[i+j+l]*(*W++)%mod;
                    a[i+j+l]=dec(a[i+j],tmp);
                    a[i+j]=add(a[i+j],tmp);
                }
            }
        }
        if(flag==-1){
            reverse(a+1,a+limit);
            ll inv=ksm(limit,mod-2);
            for(ri i=0;i<limit;++i) a[i]=a[i]*inv%mod;
        }
    }
    ll _F[N];
    void rev(int limit,int ws){
        for(ri i=0;i<limit;i+=2){
            r[i]=r[i>>1]>>1;
            r[i|1]=(r[i>>1]>>1)|(1<<ws-1);
        }
    }
    void NTT(int n,ll *f,int m,ll *g,int lim=0){
        if(!lim) lim=n+m;
        int limit=1,ws=0;
        for(;limit<=n+m;++ws,limit<<=1);
        rev(limit,ws);
        for(ri i=0;i<=m;++i) _F[i]=g[i];
        for(ri i=m+1;i<limit;++i) _F[i]=0;
        for(ri i=n+1;i<limit;++i) f[i]=0;
        DFT(limit,f,1),DFT(limit,_F,1);
        for(ri i=0;i<limit;++i) f[i]=f[i]*_F[i]%mod;
        DFT(limit,f,-1);
        for(ri i=lim+1;i<limit;++i) f[i]=0;
    }
}
using namespace Poly;
ll f[N],g[N],s[N],F[N],G[N],S[N];
int n;
/*
0 l+1
mid-l mid+1
r-l-1 r
*/
void solve(int l,int r){
    if(l==r){
        if(l==1) f[1]=1;
        return;
    }
    int mid=l+r>>1;
    solve(l,mid);
    for(ri i=0;i<=(r-l)*2;++i) S[i]=G[i]=0;
    for(ri i=0;i<=r-l-1;++i) S[i]=s[i];
    for(ri i=0;i<=mid-l;++i) G[i]=g[l+i];
    NTT(mid-l,G,r-l-1,S);
    for(ri i=mid-l;i<r-l;++i) f[i+l+1]=add(f[i+l+1],G[i]*inv[i+l+1]%mod*inv2%mod);
    for(ri i=0;i<=(r-l)*2;++i) S[i]=G[i]=0;
    for(ri i=0;i<=r-l-1;++i){
        if(i+1<l) S[i]=add(f[i+1],f[i+1]);
        else if(i+1<=mid) S[i]=f[i+1];
    }
    for(ri i=0;i<=mid-l;++i) G[i]=f[l+i];
    NTT(mid-l,G,r-l+1,S);
    for(ri i=mid-l;i<r-l;++i) g[i+l+1]=add(g[i+l+1],G[i]);
    solve(mid+1,r);
}
char ch[N];
int main(){
    // freopen("ex_reaction3.in","r",stdin);
    // freopen("1.out","w",stdout);
    init();
    n=read();
    scanf("%s",ch);
    for(ri i=0;i<n;++i) 
        if(ch[i]=='1') s[i]=ifac[i];
    solve(1,n);
    for(ri i=1;i<=n;++i) print(f[i]*fac[i]%mod);
    return 0;
}

标签:UOJ50,链式反应,ll,mid,il,frac,aligned,sum
来源: https://www.cnblogs.com/Guts/p/15182701.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有