看A,B,C看上去都不是我擅长的题目。
C感觉要会猜结论,A,B都是计数。
冷静一下,看A发现可以枚举每个操作的摆放位置,求出对答案的贡献。
求贡献可以用一个被化成多项式的dp解决。
然后直接做只能(O(n^2))
再想了很久才发现在多项式内相同的项可以在一起维护,这样子可以用分治fft在(O(nlog_2^2n))时间内解决。
然后看B,只会暴力。
然后还有40分(其实是20分)的暴力dp+莫反(不用fwt)
由于时间不多了,所以就只写了暴力dp+莫反。
看C好像只会暴力。
然后最后测评时发现自己的暴力dp+莫反re了前两个点。
这是因为读错部分分。
总结:
发挥一般,有失误但是不大。
B没有什么时间思考,而且读错部分分是一个失误。
由于读错n,(max{a_i})的失误,导致没有想出正解。
C这种图论题本来自己就不擅长。
而且NOI/省选出这种题肯定没什么人得高分。
所以自己在这题上没有出现失误。
题解:
A:
考虑枚举每一个数放的位置(i),计算它的贡献。
显然它的贡献就是它后面的乘法操作的个数(*frac{i!}{(n-i)!})。
考虑dp求解。设(f_{i,j})表示放置前(i)个数,有(j)个数对答案产生贡献的方案。
(f_{i,j}=p_if_{i-1,j}+(1-p_i)b_if_{i-1,j-1})
设(F_i=sum x^jf_{i,j})
则(F_i=((1-p_i)b_ix+p_i)F_{i-1})
(sum F_{n-1}[x^j]frac{j!}{(n-j)!})就是答案。
发现(F_i)表示(prod ((1-p_i)b_ix+p_i))除以一项,所以把这些数都乘起来然后每次除就有(50)分了。
注意到对于每个(x^j),对答案的贡献都是相同的。
可以用分治fft处理出多项式除以每个单项式的值然后求出答案。
#include<bits/stdc++.h>
using namespace std;
#define mo 998244353
#define N 500010
#define ll unsigned long long
#define int long long
#define pl vector<int>
int qp(int x,int y){
int r=1;
for(;y;y>>=1,x=1ll*x*x%mo)
if(y&1)r=1ll*r*x%mo;
return r;
}
int n,m,rev[N],v,le,w[N];
void deb(pl x){
for(int i:x)cout<<i<<' ';
puts("");
}
void init(int n){
v=1;
le=0;
while(v<n)le++,v*=2;
for(signed i=0;i<v;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(le-1));
int g=qp(3,(mo-1)/v);
w[v/2]=1;
for(int i=v/2+1;i<v;i++)
w[i]=1ull*w[i-1]*g%mo;
for(signed i=v/2-1;~i;i--)
w[i]=w[i*2];
}
void fft(int v,pl &a,int t){
static unsigned long long b[N];
int s=le-__builtin_ctz(v);
for(int i=0;i<v;i++)
b[rev[i]>>s]=a[i];
int c=0;
w[0]=1;
for(signed i=1;i<v;i*=2,c++)
for(signed r=i*2,j=0;j<v;j+=r)
for(signed k=0;k<i;k++){
int tx=b[j+i+k]*w[k+i]%mo;
b[j+i+k]=b[j+k]+mo-tx;
b[j+k]+=tx;
}
for(int i=0;i<v;i++)
a[i]=b[i]%mo;
if(t==0)return;
int iv=qp(v,mo-2);
for(signed i=0;i<v;i++)
a[i]=1ull*a[i]*iv%mo;
a.resize(v);
reverse(a.begin()+1,a.end());
}
pl operator *(pl x,pl y){
int s=x.size()+y.size()-1;
if(x.size()<=30||y.size()<=30){
pl r;
r.resize(s);
for(int i=0;i<x.size();i++)
for(int j=0;j<y.size();j++)
r[i+j]=(r[i+j]+x[i]*y[j])%mo;
return r;
}
init(s);
x.resize(v);
y.resize(v);
fft(v,x,0);
fft(v,y,0);
for(int i=0;i<v;i++)
x[i]=x[i]*y[i]%mo;
fft(v,x,1);
x.resize(s);
return x;
}
void ad(pl &x,pl y,int l){
x.resize(max((int)x.size(),(int)y.size()+l));
for(int i=0;i<y.size();i++)
x[i+l]=(x[i+l]+y[i])%mo;
}
pl operator +(pl x,pl y){
ad(x,y,0);
return x;
}
int p[N],a[N],b[N],jc[N],ij[N];
pl c[N],d[N],s[N];
void fz(int o,int l,int r){
if(l==r){
d[o].resize(2);
d[o][0]=1;
d[o][1]=b[l];
pl va;
va.resize(1);
va[0]=a[l];
s[o]=va;
return;
}
int md=(l+r)/2;
fz(o*2,l,md);
fz(o*2+1,md+1,r);
d[o]=d[o*2]*d[o*2+1];
s[o]=s[o*2]*d[o*2+1]+s[o*2+1]*d[o*2];
}
signed main(){
freopen("operation.in","r",stdin);
freopen("operation.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&p[i],&a[i],&b[i]);
int c=a[i],d=b[i];
a[i]=p[i]*c%mo;
b[i]=(b[i]+p[i]-p[i]*b[i]%mo+mo)%mo;
}
jc[0]=1;
for(int i=1;i<N;i++)
jc[i]=jc[i-1]*i%mo;
ij[N-1]=qp(jc[N-1],mo-2);
for(int i=N-1;i;i--)
ij[i-1]=ij[i]*i%mo;
int ans=0;
fz(1,1,n);
for(int i=0;i<n;i++)
ans=(ans+jc[i]*jc[n-i-1]%mo*s[1][i]%mo)%mo;
printf("%lld",ans*ij[n]%mo);
}
B:
考虑莫反,我们要计算(sum mu(i)*)提取被(i)整除的数后,(b_1...b_k)xor起来(=s)的个数。
设(c_i)表示(i)出现的次数
发现(max(a_i)=v)比较小,但是(n)比较大,所以从(c)的方面进行考虑。
对于每个数(x)把倍数对应的(c)提取出来。
当提取出来的元素较多((geq sqrt{max(a_i)}))时显然可以(fwt)计算。
当提取出来的元素较少时,可以考虑折半。
预处理出前(2)个数,后(2)个数的方案数,然后枚举左边的取值,右边的取值就是(s^)左边的取值。
但是我们算出的答案可能下标会重复 ,需要容斥。
考虑一张点数为(4)的图,如果下标重复则把对应的下标连边。
然后发现每个连通块一定要是相同的。
然后可以使用前面的方法进行计算。
时间复杂度(O(vsqrt{v}log_2v))