题目描述
给出初始点(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);
}