【题目】
原题地址
给定一个长度为
n
n
n的二进制,初始每一位都为
0
0
0,现在有
m
m
m个操作,第
i
i
i个操作是给它加上
2
a
i
2^{a_i}
2ai,操作的代价是这次操作改变的位的数量。对于第
i
i
i个操作有
p
i
p_i
pi的概率执行,问操作代价和的期望。
【解题思路】
一道并不是很难的概率题,因为一个小地方符号写错想了半天。
首先分析题目,每个操作是独立的,因此我们可以分开考虑,那么就会分成两部分:对于某位加
1
1
1,进位。
首先考虑对于某位加
1
1
1的操作,我们设
f
i
,
j
f_{i,j}
fi,j表示第
i
i
i位仅通过在这位上的操作贡献
j
j
j次的概率,那么第
i
i
i位新加进来一个操作,我们有:
f
i
,
j
′
=
(
1
−
p
)
×
f
i
,
j
+
p
×
f
i
,
j
−
1
f'_{i,j}=(1-p)\times f_{i,j}+p\times f_{i,j-1}
fi,j′=(1−p)×fi,j+p×fi,j−1,那么我们将第二维用一个多项式来表示,则相当于乘上
(
(
1
−
p
)
+
p
x
)
((1-p)+px)
((1−p)+px)这个多项式。特别地,一开始
f
i
,
0
=
1
f_{i,0}=1
fi,0=1。于是加操作我们可以用堆启发式合并
+
NTT
+\text{NTT}
+NTT在
O
(
m
l
o
g
2
m
)
O(mlog^2m)
O(mlog2m)的时间内处理完。
现在考虑进位,我们设
g
i
,
j
g_{i,j}
gi,j表示第
i
i
i位一共
j
j
j次的概率,那么我们不难得到:
g
i
,
j
=
∑
⌊
a
2
⌋
+
b
=
j
g
i
−
1
,
a
×
f
i
,
b
g_{i,j}=\sum_{\lfloor \frac a 2 \rfloor + b=j} g_{i-1,a}\times f_{i,b}
gi,j=∑⌊2a⌋+b=jgi−1,a×fi,b
于是这个也是一个卷积的形式,我们分开奇数位和偶数位考虑,仍然用
NTT
\text{NTT}
NTT就可以做到
O
(
m
l
o
g
m
)
O(mlogm)
O(mlogm),这个复杂度是因为实际上每次操作影响到的位数最多就是
l
o
g
log
log级别的(这个相当于暴力233)。
所以总的复杂度是
O
(
m
l
o
g
2
m
)
O(mlog^2m)
O(mlog2m)的。
有点卡常其实。LOJ评测语言选c++(NOI)过不了。
【参考代码】
#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
#define vi vector<int>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=2e5+10,M=N<<2;
const int mod=998244353,G=3;
int n,Q,ans;
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void up(int &x,int y){x+=y;if(x>=mod)x-=mod;if(x<0)x+=mod;}
int upm(int x){return x>=mod?x-mod:x;}
int qpow(int x,int y)
{
int ret=1;
for(;y;y>>=1,x=(ll)x*x%mod) if(y&1) ret=(ll)ret*x%mod;
return ret;
}
namespace NTT
{
int rem,m,L,tot,rev[M],cnt[M];
vi f[N],h,g;
priority_queue<pii>q[N];
void ntt(vi &a,int n,int op)
{
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 wn=qpow(G,(mod-1)/(i<<1));
if(op==-1) wn=qpow(wn,mod-2);
for(int j=0;j<n;j+=(i<<1))
{
int w=1;
for(int k=0;k<i;++k,w=(ll)w*wn%mod)
{
int x=a[j+k],y=(ll)w*a[i+j+k]%mod;
a[j+k]=upm(x+y);a[i+j+k]=upm(x-y+mod);
}
}
}
if(op==-1) for(int i=0,inv=qpow(n,mod-2);i<n;++i) a[i]=(ll)a[i]*inv%mod;
}
void reget(int sz)
{
for(m=1,L=0;m<=sz;m<<=1) ++L; if(rem==m) return; rem=m;
for(int i=0;i<m;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
}
void merge(int idx,int idy)
{
int sz=f[idx].size()+f[idy].size()-1;
reget(sz);
f[idx].resize(m);f[idy].resize(m);
ntt(f[idx],m,1);ntt(f[idy],m,1);
for(int i=0;i<m;++i) f[idx][i]=(ll)f[idx][i]*f[idy][i]%mod;
ntt(f[idx],m,-1);f[idx].resize(sz);
//for(int i=0;i<sz;++i) f[idx][i]=C[i];
}
void calc(int k)
{
if(!q[k].empty())
{
int a=q[k].top().se;
for(int i=0;i<=cnt[k];++i) h[i]=f[a][i];
}
else h[0]=1;
//printf("%d||",k);
//for(int i=0;i<=cnt[k];++i) printf("%d ",h[i]); puts("");
reget(tot);
for(int i=cnt[k]+1;i<m;++i) h[i]=0;
//for(int i=0;i<m;++i) printf("%d ",g[i]); puts("");
//for(int i=0;i<m;++i) printf("%d ",h[i]); puts("");
ntt(g,m,1);ntt(h,m,1);
for(int i=0;i<m;++i) g[i]=(ll)g[i]*h[i]%mod;
ntt(g,m,-1);
for(int i=1;i<=tot;i+=2) up(ans,mod-g[i]);
for(int i=1;i<=tot;++i) up(g[i>>1],g[i]),g[i]=0;
//for(int i=0;i<=tot;++i) printf("%d ",g[i]); puts("");
//printf("%d\n",ans);
}
void solve()
{
n+=30;tot=0;
g.resize(N<<1);h.resize(N<<1);g[0]=1;
for(int k=0;k<n;++k)
{
tot=tot/2+cnt[k];
if(!tot) continue;
while(q[k].size()>1)
{
int a=q[k].top().se;q[k].pop();
int b=q[k].top().se;q[k].pop();
merge(a,b);q[k].push(mkp(-f[a].size(),a));
}
calc(k);
}
printf("%d\n",ans);
cerr<<clock()<<endl;
}
void init()
{
n=read();Q=read();
for(int i=1;i<=Q;++i)
{
int a=read(),p=read();p=(ll)p*qpow(read(),mod-2)%mod;
++cnt[a];up(ans,upm(p+p));
f[i].resize(2);f[i][0]=upm(1-p+mod);f[i][1]=p;q[a].push(mkp(-2,i));
}
}
};
int main()
{
#ifndef ONLINE_JUDGE
freopen("LOJ565.in","r",stdin);
freopen("LOJ565.out","w",stdout);
#endif
NTT::init();NTT::solve();
return 0;
}