A. 随 (rand)
尽量不要重载乘法,真的很慢。
50分算法因为重载乘法被卡常卡成20分,真的很伤。
$void$函数,传入希望存储答案的指针,使用$memcpy$快速传递。
1 void mult(const matrix &a,const matrix &b,matrix *x)
2 {
3 for(int i=1;i<p;++i)
4 {
5 now.s[i]=0;
6 for(int j=1;j<p;++j) now.s[i]+=a.s[j]*b.s[i-j+1<=0?i-j+p:i-j+1]%mod;
7 now.s[i]%=mod;
8 }
9 memcpy(x,&now,sizeof(matrix));
10 }
11 void matrix_qpow(matrix &base,int k,matrix *x)
12 {
13 ans.s[1]=1;
14 while(k)
15 {
16 if(k&1) mult(ans,base,&ans);
17 mult(base,base,&base);
18 k>>=1;
19 }
20 memcpy(x,&ans,sizeof(matrix));
21 }
题中求期望数,可以很简单的转化为每个方案数乘该方案的价值除以总方案数。
设$dp(i,j)$表示使用i次操作,对p取余为j。
可以列出简单的转移,复杂度$O(m*p^2)$
当m大到1e9量级,选择只有两个:
删掉枚举m,或者把关于m的缩为$logm$。
使用后者,矩阵快速幂即可,新的复杂度$O(p^3*logm)$。
然而$p^3$太大了,仍然无法接受。
优化的一个方法是循环矩阵,然而目前并不满足循环矩阵的性质。
题中提示使用原根,然而考场上刚刚接触原根,
还不明白原根是什么,更别提使用。
p的原根满足原根的1~p-1次方在模p意义下取互不相同的值。
于是乘法可以被转化为加法,
原来矩阵中1行i列表示的是取余为i的方案数,转变为取余为$root^i%p$的方案数。
显然新的状态定义是满足循环矩阵性质的,可以$O(n^2logm)$计算。
B. 单(single)
对于t=0,O(n)树形dp统计根节点的答案,
考虑换根的答案改变量,再一次dfs换根即可。
对于t=1,
n<=100的数据,直接列式高斯消元。
通过t=0时换根的处理办法,我们发现父子节点b值的差是很好的。
设$s(x)$表示x及x子树a值的和,根为1。
有$b(x)-b(fa)=(s(1)-s(x))-s(x)=s(1)-2*s(x)$
对除1外的每个点求和,则右式中$s(1)$出现n-1次,$s(2)~s(n)$中的每个出现-2次。
根据定义和数据点5的提示,发现$b(1)=\sum \limits_{i=2}^{n}s(i)$。
恰好是右式中的式子,于是相加得到了$s(1)$,
向子树递推即可得到每个点的s值,进而得到a值。
C. 题(problem)
情况0:
无限制,枚举向左移动个数,则确定一种步数的方案。
$l=r, u=d$
$l+r+u+d=n$
对于l的每个取值,方案数为:
$\frac{n!}{l!*r!*u!*d!}$
求和即可。
情况1:
只允许到达x轴非负半轴的点。
显然答案即卡特兰数。
情况2:
只允许到达坐标轴上的点,但数据范围只开到1000。
$O(n^2)$ $dp$即可。
情况3:
只允许到达x,y轴非负半轴和第一象限的点,
与情况1接近,
枚举向上移动的步数,答案就是选该步数的组合数和两个卡特兰数的乘积,求和。