alpha - 分治NTT - 线段树

题目大意:
有[1,1e9]的数轴,n次操作每次给一个区间[Li,Ri]的数有pi的概率+1,1-pi的概率不变。问最后等于k的数字期望有多少个。 n ≤ 5 × 1 0 4 n\le5\times10^4 n5×104
题解:
问题等价于区间乘以一个一次式,最后对每个位置求k次项系数。
离散化后线段树,等价于每次从根到叶节点的式子乘起来在乘个叶节点对应的区间长度取k次项系数求和。但这么做复杂度是错的,考虑反过来从下到上合并,每个节点的初始信息用分治NTT算一下即可,这样复杂度显然不会超过三个log。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define p 998244353
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
vector<int> vec;
inline int getid(int x) { return lower_bound(vec.begin(),vec.end(),x)-vec.begin()+1; }
inline int clrv(vector<int> &v) { vector<int>().swap(v);return 0; }
inline int fast_pow(int x,int k,int ans=1) { for(;k;k>>=1,x=(lint)x*x%p) (k&1)?ans=(lint)ans*x%p:0;return ans; }
inline int show(const vector<int> &A)
{
    int n=(int)A.size();debug(n)sp;
    rep(i,0,n-1) cerr<<A[i]sp;cerr ln;
    return 0;
}
namespace NTT_space{
    const int N=400010;
    int a[N],b[N],r[N];
    inline int clr(int *a,int n) { return memset(a,0,sizeof(int)*(n)),0; }
    inline int NTT(int *a,int n,int s)
    {
        rep(i,1,n-1) if(i<r[i]) swap(a[i],a[r[i]]);
        for(int i=2;i<=n;i<<=1)
        {
            int wn=fast_pow(3,(s>0)?(p-1)/i:p-1-(p-1)/i);
            for(int j=0,t=i>>1;j<n;j+=i)
                for(int k=0,w=1,x,y;k<t;k++,w=(lint)w*wn%p)
                    x=a[j+k],y=(lint)w*a[j+k+t]%p,
                    a[j+k]=x+y,(a[j+k]>=p?a[j+k]-=p:0),
                    a[j+k+t]=x-y,(a[j+k+t]<0?a[j+k+t]+=p:0);
        }
        if(s<0) for(int i=0,ninv=fast_pow(n,p-2);i<n;i++) a[i]=(lint)a[i]*ninv%p;
        return 0;
    }
    inline vector<int> pls(const vector<int> &A,const vector<int> &B)
    {
        int n=(int)A.size(),m=(int)B.size();vector<int> C(max(n,m));
        Rep(i,C) C[i]=(i<n?A[i]:0)+(i<m?B[i]:0),(C[i]>=p?C[i]-=p:0);
        return C;
    }
    inline vector<int> tms(const vector<int> &A,const vector<int> &B)
    {
        int m1=(int)A.size(),m2=(int)B.size();
        vector<int> C(m1+m2-1);int n=1,L=0;
        while(n<m1+m2-1) n<<=1,L++;
        rep(i,0,n-1) r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));
        rep(i,0,m1-1) a[i]=A[i];clr(a+m1,n-m1);
        rep(i,0,m2-1) b[i]=B[i];clr(b+m2,n-m2);
        NTT(a,n,1),NTT(b,n,1);
        rep(i,0,n-1) a[i]=(lint)a[i]*b[i]%p;
        NTT(a,n,-1);
        rep(i,0,m1+m2-2) C[i]=a[i];return C;
    }
    inline vector<int> solve(vector<int> &pv,int l,int r)
    {
        if(l==r) { vector<int> ans(2);ans[0]=1-pv[l]+p,ans[1]=pv[l];return ans; }
        int mid=(l+r)>>1;return tms(solve(pv,l,mid),solve(pv,mid+1,r));
    }
}
using NTT_space::pls;
using NTT_space::tms;
using NTT_space::solve;
struct segment{
    vector<int> pv,pl;
    segment *ch[2];
}*rt;
int build(segment* &rt,int l,int r)
{
    rt=new segment,clrv(rt->pv),clrv(rt->pl);if(l==r) return 0;int mid=(l+r)>>1;
    return build(rt->ch[0],l,mid),build(rt->ch[1],mid+1,r),0;
}
int update(segment* &rt,int l,int r,int s,int t,int v)
{
    if(s<=l&&r<=t) return rt->pv.pb(v),0;int mid=(l+r)>>1;
    if(s<=mid) update(rt->ch[0],l,mid,s,t,v);
    if(mid<t) update(rt->ch[1],mid+1,r,s,t,v);
    return 0;
}
int dfs(segment* &rt,int l,int r)
{
    if(rt->pv.size()) rt->pl=solve(rt->pv,0,(int)rt->pv.size()-1),clrv(rt->pv);
    else rt->pl.resize(1),rt->pl[0]=1;
    if(l==r)
    {
        Rep(i,rt->pl) rt->pl[i]=(lint)rt->pl[i]*(vec[l]-vec[l-1])%p;
        return 0;
    }
    int mid=(l+r)>>1;dfs(rt->ch[0],l,mid),dfs(rt->ch[1],mid+1,r);
    rt->pl=tms(rt->pl,pls(rt->ch[0]->pl,rt->ch[1]->pl));
    return clrv(rt->ch[0]->pl),clrv(rt->ch[1]->pl),0;
}
const int N=50010;
int L[N],R[N],pv[N];
int main()
{
    int n=inn();
    rep(i,1,n) L[i]=inn(),R[i]=inn(),pv[i]=inn(),vec.pb(L[i]),vec.pb(R[i]+1);
    sort(vec.begin(),vec.end());
    vec.erase(unique(vec.begin(),vec.end()),vec.end());
    rep(i,1,n) L[i]=getid(L[i]),R[i]=getid(R[i]+1);
    int m=(int)vec.size();vec.pb(1e9+1);
    build(rt,1,m);
    rep(i,1,n) update(rt,1,m,L[i],R[i]-1,pv[i]);
    dfs(rt,1,m);
    return !printf("%d\n",rt->pl[inn()]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值