【LOJ】#565. 「LibreOJ Round #10」mathematican 的二进制-分治NTT

这篇博客介绍了如何利用关键性质和分治NTT(Number Theoretic Transform)方法解决LOJ565题目中的数学问题。操作次数的期望和最终数值1的位数期望可以通过操作次数的生成函数和数的贡献的生成函数计算,复杂度为O(mlog2m)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:loj565

发现关键了性质就很简单。


题解

关键性质:
一系列操作的总贡献=操作次数 × 2 − bit ( v a l ) \times 2-\text{bit}(val) ×2bit(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+1pi),分治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+1pk)(ak=i),依旧分治NTT处理。
f i , j f_{i,j} fi,j表示考虑进位,第 i i i位被加了 j j j次的概率,假设已经处理好了 f i − 1 f_{i-1} fi1 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=jfi1,agi,b,相当于 f i − 1 f_{i-1} fi1每项系数转移到整除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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值