<span>数学专题测试1 题解</span>
A. 解方程
考虑没有任何限制的东西,
m个元素分为n个连续的区间,直接用组合数插板法就好了。
考虑如何去掉元素大于$a_i$的限制,
只要给最终的元素个数减掉$a_i$就好了。
考虑如何搞元素个数小于$a_i$的限制,
不妨使用子集反演,要求的是恰好$0$个元素大于$a_i$。
只要钦定其中的每个集合元素大于$a_i$,问题就转化为了容易解决的问题,乘上容斥系数就好了。
因为模数满足$p^k$并不大,使用$exlucas$(实际上就是不断提取$p$这个质因子)就好了。
B. 宇宙序列
因为异或的性质$a\ xor\ b\ xor\ b=a$,题中给出的式子可以转化为异或卷积式。
因为异或卷积具有结合律,可以直接倍增求,使用FWT优化,可以做到$O(pn2^n)$。
通过DFT,可以将异或的卷积式转化为对位相乘。
暴力相加并不容易优化,但是可以发现对位相乘的结果的形式是优美的。
根据异或FWT的DFT性质(可以结合或卷积的子集和理解),可以发现不断在系数/点值两种情况下转换是没有意义的。
只要在点值意义下不断的自乘,累加在另一个数组中就可以了。
设$a$数组DFT之后为$A$数组,那么对于$A_i$,对应的累加结果为$B_i=\sum \limits_{j=0}^{p}A_i^{2^j}$。
问题在于求出$B$,一次性IDFT回去就好了。
利用本题中模数并不大($10007$)的性质,找一下循环节可以做到$O(mod^2)$预处理。
然而正解是一个倍增。
设$f_{x,k}=\sum \limits_{j=0}^{2^k-1}x^{2^j}$
有$f_{x,k}=f_{x,k-1}+f_{x^{2^{2^{k-1}}},k-1}$
询问的时候只要用类似的方法倍增统计就好了。
用一些特殊的技巧,可以将总复杂度做到$O(mod*logp+n*2^n)$。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int mod=10007; 4 const int inv2=5004; 5 const int N=262150; 6 int n,p,d,lim; 7 int a[N],f[mod][32]; 8 inline void fwt(int opt){ 9 for(int i=1;i<lim;i<<=1) 10 for(int j=0;j<lim;j+=i<<1) 11 for(int k=0;k<i;++k){ 12 int x=a[j+k],y=a[j+k+i]; 13 a[j+k]=(x+y)%mod; a[j+k+i]=(x-y+mod)%mod; 14 if(opt==-1) a[j+k]=a[j+k]*inv2%mod,a[j+k+i]=a[j+k+i]*inv2%mod; 15 } 16 } 17 inline int qpow(int x,int k,int mod,int r=1){ 18 for(;k;k>>=1,x=x*x%mod) if(k&1) r=r*x%mod; 19 return r; 20 } 21 inline int query(int x,int k,int ans=0,int r=0){ 22 for(int i=0;i<30;++i) if(k>>i&1) ans+=f[qpow(x,qpow(2,r%5002,mod-1),mod)][i],r+=1<<i; 23 return ans%mod; 24 } 25 inline int read(register int x=0,register char ch=getchar(),register int f=0){ 26 for(;!isdigit(ch);ch=getchar()) f=ch=='-'; 27 for(; isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48); 28 return f?-x:x; 29 } 30 int main(){ 31 n=read(); p=read(); d=read(); lim=1<<n; 32 for(int i=0;i<lim;++i) a[i]=read(); 33 for(int i=0;i<mod;++i) f[i][0]=i; 34 for(int i=1;i<30;++i) for(int j=0;j<mod;++j) f[j][i]=(f[j][i-1]+f[qpow(j,qpow(2,(1<<i-1)%5002,mod-1),mod)][i-1])%mod; 35 fwt(1); 36 for(int j=0;j<lim;++j) a[j]=query(a[j],p+1); 37 fwt(-1); 38 printf("%d\n",a[d]); 39 return 0; 40 }
C. exp
不会做+看不懂,就没什么好说的。
UPD:大概看懂了新题解,并颓了std,所以AC了。
可以发现,对于区间$[l,r]$,找出其中的一个O,设下标为$k$。
可以划分为$[l,k]$,$[k+1,r]$两个区间,在不选择$k$的情况下,可以视为两个区间互不干涉,于是可以以此为依据进行DP。
设$f_{l,r}$表示消完区间$[l,r-1]$,最后一个O下标为$r$,还没有消对应的期望。
$g_{l,r}$与上述定义相同,但为概率。
$p(l,r,k)$表示区间$[l,k-1]$已经消完,区间$[k+1,r-1]$已经消完,$k$最后一个被消为X,$r$还没有消的概率。
对于$p(l,r,k)$,设区间$[l,k-1]$中有$lc$个O,区间$[k+1,r-1]$中有$rc$个O。
有$p(l,r,k)=\binom{lc+rc}{lc}*(\frac{k-l+1}{r-l+1})^{lc}*g_{l,k}*(\frac{r-k}{r-l+1})^{rc}*g_{k+1,r}*\frac{k-l+1}{r-l+1}$
为了便于理解,可以考虑一个特殊情况:$k-l+1=r-k$,有选择右侧概率和选择右侧概率相同。
那么原式可以写为$p(l,r,k)=\frac{\binom{lc+rc}{lc}}{2^{lc+rc}}*g_{l,k}*g_{k+1,r}*\frac{1}{2}$,这里的概率即选择左右次数恰好合法的方案除以每个不同的方案。
对于选择左侧和右侧概率不同的情况,也可以在方案数的角度考虑。
首先考虑使选择左右次数分别合法的方案数:
$(k-l+1)^{lc}*(r-k)^{rc}*\binom{lc+rc}{lc}$,即首先在左侧选择$lc$次,再在右侧选择$rc$次。
然而并没有要求左右的严格顺序,所以要乘一个组合数。
使最后一次合法,贡献为乘$(k-l+1)$。
总的方案数是$(r-l+1)^{lc+rc+1}$,所以这个式子就推出来了。
$g_{l,r}=\sum \limits_{k=l}^{r-1}p(l,r,k)$
$f_{l,r}=\frac{1}{g_{l,r}}\sum\limits_{k=l}^{r-1} p(l,r,k)*(f_{l,k}+f_{k+1,r}+\frac{k-l}{2})$
因为有上式$g$数组的求和式,$f$的转移式实际上是期望的一个加权平均。
考虑最后一次操作补上$k$位置对期望的贡献,设$n=k-l+1$。
有$E=\frac{\sum \limits_{i=0}^{n-1}i}{n}=\frac{n-1}{2}$。
原式中的$\frac{k-i}{2}$与上述的$\frac{n-1}{2}$比较类似,最后的一次只在左侧选择,即将$n=k-i+1$代入。
当$[l,r]$区间内仅有$r$为O,有初态$f_{l,r}=0$,$g_{l,r}=1$。
统计答案考虑断环成链,最后一次贡献的期望是$\frac{n-1}{2}$,枚举最后一个变为X的O。
$ans=\sum \limits_{i=1}^{n}g_{i,i+n-1}*f_{i,i+n-1}$
因为$\sum \limits_{i=1}^ng_{i,i+n-1}=1$,这样不除总概率(等于1)的做法是合理的。