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 }
View Code

 

题中求期望数,可以很简单的转化为每个方案数乘该方案的价值除以总方案数。

设$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接近,

枚举向上移动的步数,答案就是选该步数的组合数和两个卡特兰数的乘积,求和。