题目描述
给出初始点(x0,y0),你可以走T步,每次上下左右,最终你会走到一个点(x,y),这个点的贡献是 xnym x n y m ,问所有方案的贡献和。
解题思路
考虑40分怎么做,可以枚举一个(x,y),算出到这里的方案数,然后乘上贡献。
另一个思路是dp地维护第i步的贡献和。
考虑某个方案往左右走,(x-1,y)+(x+1,y)的贡献,二项式展开 ((x+1)n+(x−1)n)ym=ym∑i=0..n,i≡0(mod 2)2Cn−inxn−i ( ( x + 1 ) n + ( x − 1 ) n ) y m = y m ∑ i = 0.. n , i ≡ 0 ( m o d 2 ) 2 C n n − i x n − i ,奇数次数的项都被消掉了。我们可以通过这种办法来维护出新的(x,y)的贡献和。
具体地,设f[t][i][j]表示走了t步,对于当前所有方案(x,y)们, xiyj x i y j 的和是多少。由于二项式定理的特性,而且和互相不干扰,我们可以把所有x,y放到一起存,存次数小于n,m的项是为了能够维护出 xnym x n y m 。x和y实际可以分开维护,因为我们可以在最后组合起来。
我们现在的任务是得出 xn x n 的和。设f[i][j]表示走了i步, xj x j 的和是多少。
转移 f[i][j]=∑k≡0(mod 2)2Ckjf[i−1][j−k] f [ i ] [ j ] = ∑ k ≡ 0 ( m o d 2 ) 2 C j k f [ i − 1 ] [ j − k ] ,微观地,就是(x,y)变成了(x-1,y),(x+1,y)。
我们分别得出 fx[i][n] f x [ i ] [ n ] 和 fy[j][m] f y [ j ] [ m ] 之后,枚举左右走i次,那么上下走t-i次,然后就可以用两个f组合起来算答案了, ans=∑if[i][n]∗f[t−i][m]∗Cit a n s = ∑ i f [ i ] [ n ] ∗ f [ t − i ] [ m ] ∗ C t i 。
不过t太大,这样没有办法做。
一种思路是旋转45度,这样x,y可以看成一样的东西,搞矩阵乘法优化dp之后求答案。这样能通过80%
考虑转移特性。对于原本的一个f[0][j],我们观察它会被哪些f状态计算,即它会被怎么转移出去。发现转移到下一层,有时候他会乘2然后下标不变,即f[i][j]->f[i+1][j],那么t这么大而n这么小,肯定很多时候都下标不变,更关键的是,他在任何地方停留,转移系数都是2,那么我们不考虑停留在原地的贡献,只计算下标变大的转移,设次数cnt,最后再乘上 2t−cnt 2 t − c n t ,那么我们就把层数优化到了n的级别。
仍然把最终做出来的东西叫 fx和fy f x 和 f y ,枚举左右的 cntx c n t x 和上下的 cnty c n t y ,要还原原来的 fx[n]和fy[m] f x [ n ] 和 f y [ m ] ,还需要乘上 4t−cntx−cnty 4 t − c n t x − c n t y ,表示下标不变的转移,因为既可能是左右,也可能是上下,所以不是2而是4。然后再乘上操作顺序的组合数就得到答案了。
时间复杂度 O(n3) O ( n 3 ) 左右
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
const int N=2e5+5,M=205,mo=1e9+7;
int fac[N],rev[N],x,y,n,m,t,ans,i,j,k,tmp,f[M][M],g[M][M];
int ksm(int x,int y)
{int ret=1;x%=mo;while (y){if (y&1) ret=1ll*ret*x%mo;y>>=1;x=1ll*x*x%mo;}return ret;
}
void predo(int n)
{fac[0]=1;fo(i,1,n) fac[i]=1ll*fac[i-1]*i%mo;rev[n]=ksm(fac[n],mo-2);fd(i,n,1) rev[i-1]=1ll*rev[i]*i%mo;
}
int c(int n,int m)
{if (m<=1e5)return 1ll*fac[m]*rev[n]%mo*rev[m-n]%mo;int ret=1,i;fd(i,m,m-n+1)ret=1ll*ret*i%mo;return 1ll*ret*rev[n]%mo;
}
int main()
{freopen("t1.in","r",stdin);//freopen("t1pai.out","w",stdout);scanf("%d %d %d %d %d",&x,&y,&t,&n,&m);predo(1e5);fo(i,0,n) f[0][i]=ksm(x,i);fo(i,0,m) g[0][i]=ksm(y,i);fo(i,1,n)fo(j,0,n)for(k=j-2;k>=0;k-=2) f[i][j]=(f[i][j]+2ll*c(k,j)*f[i-1][k])%mo;fo(i,1,m)fo(j,0,m)for(k=j-2;k>=0;k-=2) g[i][j]=(g[i][j]+2ll*c(k,j)*g[i-1][k])%mo;ans=0;fo(i,0,min(n+m,t)){tmp=0;fo(j,0,i) if (j<=n&&i-j<=m)tmp=(tmp+1ll*f[j][n]*g[i-j][m]%mo*c(j,i))%mo;ans=(ans+1ll*c(i,t)*tmp%mo*ksm(4,t-i))%mo;}ans=(ans+mo)%mo;printf("%d\n",ans);
}