[JZOJ5998]【WC2019模拟2019.1.14】操作

Description

在这里插入图片描述
在这里插入图片描述

Solutioin

推一波式子,容易将一个操作写成一次函数的形式。

现在只需要对于每个操作计算所有可能在它后面的操作对它的影响,也就是斜率乘积乘上这种情况出现的概率。

考虑分治NTT
但此时我们发现需要对于每一个操作求和,且自己不能选。

有一个很妙的做法,分治NTT的时候对于两边分别维护全选上的和缺一个的和

左边缺一个 * 右边全选 + 右边缺一个 * 左边全选就是整个区间缺一个的和

这样就 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)做完了

Code

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define mo 998244353
#define LL long long
#define N 100005
#define M 262144
using namespace std;
int n,st[N],le[N],bit[M+1],n1,cf[21],l2[M+1];
LL a[M+1],u1[M+1],u2[M+1],u3[M+1],u4[M+1],wi[M+1],wg[M+1],ny,a1[N][3],js[N],b[M+1];
LL ksm(LL k,LL n)
{
	LL s=1;
	for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
	return s;
}
void prp(int num)
{
	fo(i,0,num)
	{
		bit[i]=(bit[i>>1]>>1)|((i&1)<<(l2[num]-1));
		wi[i]=wg[i*(M/num)];
	}
	ny=ksm(num,mo-2);
}
void NTT(LL *a,bool pd,int num)
{
	fo(i,0,num-1) if(i<bit[i]) swap(a[i],a[bit[i]]);
	LL v;
	for(int lim=num>>1,m=2,half=1;m<=num;half=m,lim>>=1,m<<=1)
	{
		LL wn=(!pd)?wi[lim]:wi[num-lim];
		for(int j=0;j<num;j+=m)
		{
			LL w=1;
			for(int i=0;i<half;++i,w=w*wn%mo)
			{
				v=w*a[i+j+half]%mo;
				a[i+j+half]=(a[i+j]-v+mo)%mo;
				a[i+j]=(a[i+j]+v)%mo;
			}
		}
	}
	if(pd) fo(i,0,num-1) a[i]=a[i]*ny%mo;
}
void doit(int l,int r)
{
	if(l==r) return;
 	int mid=(l+r)>>1;
	doit(l,mid),doit(mid+1,r);
	int num=cf[l2[le[l]+le[mid+1]]];
	fo(i,0,num-1) u1[i]=u2[i]=u3[i]=u4[i]=0;
	fo(i,0,le[l]-1) u1[i]=a[st[l]+i],u3[i]=b[st[l]+i];
	fo(i,0,le[mid+1]-1) u2[i]=a[st[mid+1]+i],u4[i]=b[st[mid+1]+i];
	prp(num);
	NTT(u1,0,num),NTT(u2,0,num),NTT(u3,0,num),NTT(u4,0,num);
	fo(i,0,num-1) 
	{
		u3[i]=(u3[i]*u2[i]+u4[i]*u1[i])%mo;
		u1[i]=u1[i]*u2[i]%mo;
	}
	NTT(u1,1,num),NTT(u3,1,num);
	le[l]=min(2*n+1,le[l]+le[mid+1]-1);
	fo(i,0,le[l]) a[st[l]+i]=u1[i],b[st[l]+i]=u3[i];
}
int main()
{
	cin>>n;
	fo(i,1,n) scanf("%lld%lld%lld",&a1[i][0],&a1[i][1],&a1[i][2]);
	LL vn=1;
	fo(i,1,n) vn=vn*(LL)i%mo;
	vn=ksm(vn,mo-2);
	fo(i,1,n)
	{
		st[i]=n1;
		le[i]=2;
		a[n1]=1;
		b[n1]=a1[i][1]*a1[i][0]%mo;
		a[++n1]=((1-a1[i][0]+mo)%mo*a1[i][2]%mo+a1[i][0])%mo;
		b[n1]=0;
		n1++;
	}
	wg[0]=1;
	LL v=ksm(3,(mo-1)/M);
	fo(i,1,M) wg[i]=wg[i-1]*v%mo;
	cf[0]=1;
	fo(i,1,18) cf[i]=cf[i-1]<<1,l2[cf[i]]=i;
	fod(i,M-1,2) if(!l2[i]) l2[i]=l2[i+1];
	doit(1,n);
	LL ans=0;
	js[0]=1;
	fo(i,1,n) js[i]=js[i-1]*(LL)i%mo;
	fo(i,0,n-1) ans=(ans+b[st[1]+i]*js[i]%mo*js[n-i-1]%mo)%mo;
	printf("%lld\n",vn*ans%mo);
}	
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值