数学专题测试1 题解

时光总嘲笑我的痴心妄想 提交于 2020-03-26 02:59:20

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 }
T2

 

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)的做法是合理的。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!