传送门:loj565
发现关键了性质就很简单。
题解
关键性质:
一系列操作的总贡献=操作次数
×
2
−
bit
(
v
a
l
)
\times 2-\text{bit}(val)
×2−bit(val)(
v
a
l
val
val代表最后得到的数)
也就是说答案与操作顺序无关,且可以分开统计操作次数的期望和最终数1的位数的期望。
操作 i i i次的概率 t i t_i ti的生成函数: ∑ t i x i = ∏ ( p i x + 1 − p i ) \sum t_ix^i=\prod(p_ix+1-p_i) ∑tixi=∏(pix+1−pi),分治NTT处理。
数的贡献可以分成单独一位和进位两步统计:
设
g
i
,
j
g_{i,j}
gi,j表示不考虑进位,第
i
i
i位被加了
j
j
j次的概率,考虑所有
a
j
=
i
a_j=i
aj=i的操作,答案还是个生成函数
∑
g
i
,
j
x
j
=
∏
(
p
k
x
+
1
−
p
k
)
(
a
k
=
i
)
\sum g_{i,j}x^j=\prod (p_kx+1-p_k)(a_k=i)
∑gi,jxj=∏(pkx+1−pk)(ak=i),依旧分治NTT处理。
设
f
i
,
j
f_{i,j}
fi,j表示考虑进位,第
i
i
i位被加了
j
j
j次的概率,假设已经处理好了
f
i
−
1
f_{i-1}
fi−1,
f
i
f_i
fi的转移即
f
i
,
j
=
∑
⌊
a
2
⌋
+
b
=
j
f
i
−
1
,
a
g
i
,
b
f_{i,j}=\sum\limits_{\lfloor\frac a2\rfloor+b=j}f_{i-1,a}g_{i,b}
fi,j=⌊2a⌋+b=j∑fi−1,agi,b,相当于
f
i
−
1
f_{i-1}
fi−1每项系数转移到整除2的那位后和
g
i
g_i
gi做了一次卷积。
总复杂度 O ( m log 2 m ) O(m\log ^2 m) O(mlog2m)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,mod=998244353,g=3;
typedef long long ll;
int n,m,a[N],p[N];
int f[N<<2],h[N<<2],rk[N],ans,ivg,mx,lim;
vector<int>hv[N];
char buf[(1<<15)];int p1=0,p2=0;
inline char gc()
{
if(p1==p2) p1=0,p2=fread(buf,1,(1<<15),stdin);
return (p1==p2)?EOF:buf[p1++];
}
char cp;
template<class T>inline void rd(T &x)
{
cp=gc();x=0;int f=0;
for(;cp<'0' || cp>'9';cp=gc()) if(cp=='-') f=1;
for(;cp>='0' && cp<='9';cp=gc()) x=x*10+(cp^48);
if(f) x=-x;
}
inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}
inline int fp(int x,int y)
{
int re=1;
for(;y;y>>=1,x=(ll)x*x%mod)
if(y&1) re=(ll)re*x%mod;
return re;
}
namespace poly{
int d[20][N<<2],rv[N<<2],L,len;
inline void ntt(int *e,int pr)
{
int i,j,k,ix,iy,pd,ori,G=pr?g:ivg;
for(i=1;i<len;++i) if(i<rv[i]) swap(e[i],e[rv[i]]);
for(i=1;i<len;i<<=1){
ori=fp(G,(mod-1)/(i<<1));
for(j=0;j<len;j+=(i<<1)){
for(pd=1,k=0;k<i;++k,pd=(ll)pd*ori%mod){
ix=e[j+k];iy=(ll)pd*e[j+i+k]%mod;
e[j+k]=ad(ix,iy);e[i+j+k]=dc(ix,iy);
}
}
}
if(pr) return;
G=fp(len,mod-2);
for(i=0;i<len;++i) e[i]=(ll)e[i]*G%mod;
}
void mul(int *f,int *g,int n,int m)
{
int i;
for(L=0,len=1;len<=n+m;len<<=1) L++;
for(i=1;i<len;++i) rv[i]=(rv[i>>1]>>1)|((i&1)<<(L-1));
for(i=n+1;i<len;++i) f[i]=0;
for(i=m+1;i<len;++i) g[i]=0;
ntt(f,1);ntt(g,1);
for(i=0;i<len;++i) f[i]=(ll)f[i]*g[i]%mod;
ntt(f,0);
}
void div(int dep,int l,int r)
{
if(l==r) {d[dep][0]=dc(1,p[rk[l]]);d[dep][1]=p[rk[l]];return;}
int i,j,mid=(l+r)>>1,lim=mid-l+1;div(dep+1,l,mid);
for(i=0;i<=lim;++i) d[dep][i]=d[dep+1][i];
div(dep+1,mid+1,r);lim=r-l+1;
mul(d[dep],d[dep+1],mid-l+1,r-mid);
}
void cal(int x)
{
int i;lim=hv[x].size();
if(!lim) {d[0][0]=1;return;}
for(i=0;i<lim;++i) rk[i+1]=hv[x][i];
div(0,1,lim);
}
}
using namespace poly;
int main(){
int i,j,x,y,pre;ivg=fp(g,mod-2);
rd(n);rd(m);
for(i=1;i<=m;++i){
rd(a[i]);mx=max(mx,a[i]);rd(x);rd(y);
p[i]=(ll)x*fp(y,mod-2)%mod;rk[i]=i;
hv[a[i]].push_back(i);
}
div(0,1,m);
for(i=1;i<=m;++i) ans=ad(ans,(ll)2*i*d[0][i]%mod);
cal(0);
for(i=0;i<=lim;++i){f[i]=d[0][i];if(i&1) ans=dc(ans,f[i]);}
for(pre=lim,i=1;i<=mx;++i){
cal(i);memset(h,0,sizeof(int)*((pre>>1)+2));
for(j=0;j<=pre;++j) h[j>>1]=ad(h[j>>1],f[j]);
pre>>=1;memcpy(f,h,sizeof(int)*(pre+2));
if(lim>0) mul(f,d[0],pre,lim);pre+=lim;
for(j=1;j<=pre;++j) if(j&1) ans=dc(ans,f[j]);
}
for(;pre>1;){
memset(h,0,sizeof(int)*((pre>>1)+2));
for(i=0;i<=pre;++i) h[i>>1]=ad(h[i>>1],f[i]);
pre>>=1;memcpy(f,h,sizeof(int)*(pre+2));
for(i=1;i<=pre;++i) if(i&1) ans=dc(ans,f[i]);
}
printf("%d",ans);
return 0;
}