a
有一个三维坐标系,从起点\((0,0,0)\)开始走,每次只能向某个坐标轴的正方向走一步,其中有m个点不能走,问走到\((n,n,n)\)的方案数。\(n\leq 10^5\),\(m\leq 5000\)
有一个递推是:设f[n][m]表示走到编号为n的点,经过了至少m个点的方案数,那么答案就是\(\sum_{i=0}^m(-1)^if[(n,n,n)][i]\times{m\choose i}\)。
考虑转移,\(f[n][m]=\sum_{i能到n}\quad f[i][m-1]\times{x_n+y_n+z_n-x_i-y_i-z_i\choose x_n-x_i}{y_n+z_n-y_i-z_i\choose y_n-y_i}\)。
发现\(ans=\sum_{n=1}^m\sum_{i=0}^m(-1)^if[n][i]\times{3n-x_n-y_n-z_n\choose n-x_n}{2n-y_n-z_n\choose n-y_n}\)
那么设\(g_n=\sum_{i=0}^m(-1)^if[n][i]\),用之前那个递推式可以求出:
\[g_n=\sum_{i能到n}-g_i\times {x_n+y_n+z_n-x_i-y_i-z_i\choose x_n-x_i}{y_n+z_n-y_i-z_i\choose y_n-y_i}
\]
就可以做到\(O(m^2)\)了。
#include<bits/stdc++.h>
#define rg register
#define il inline
#define cn const
#define gc getchar()
#define fp(i,a,b) for(rg int i=(a),ed=(b);i<=ed;++i)
#define fb(i,a,b) for(rg int i=(a),ed=(b);i>=ed;--i)
#define go(u) for(rg int i=head[u];~i;i=e[i].nxt)
#define mp make_pair
using namespace std;
typedef cn int cint;
typedef long long LL;
il int rd(){
rg int x(0),f(1); rg char c(gc);
while(c<'0'||'9'<c){if(c=='-')f=-1;c=gc;}
while('0'<=c&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
return x*f;
}
cint maxn=100010,maxm=5010,mod=1e9+7;
int n,m,x[maxm],y[maxm],z[maxm];
int f[maxm],fac[maxn*3],ifac[maxn*3],ans;
il int fpow(int a,int b,int ans=1){
for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ans=1ll*ans*a%mod;
return ans;
}
il int C(cint &n,cint &m){return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;}
int dfs(int u){
if(f[u])return f[u];
fp(i,0,m)if(i!=u&&x[i]<=x[u]&&y[i]<=y[u]&&z[i]<=z[u]){
f[u]=(f[u]-1ll*C(x[u]-x[i]+y[u]-y[i]+z[u]-z[i],x[u]-x[i])*C(y[u]-y[i]+z[u]-z[i],y[u]-y[i])%mod*dfs(i)%mod+mod)%mod;
}
return f[u];
}
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
n=rd(),m=rd();
fp(i,1,m)x[i]=rd(),y[i]=rd(),z[i]=rd();
fac[0]=1; fp(i,1,n*3)fac[i]=1ll*fac[i-1]*i%mod;
ifac[n*3]=fpow(fac[n*3],mod-2);
fb(i,n*3,1)ifac[i-1]=1ll*ifac[i]*i%mod;
f[0]=1;
fp(i,1,m)if(!f[i])f[i]=dfs(i);
fp(i,0,m)ans=(ans+1ll*f[i]*C(n*3-x[i]-y[i]-z[i],n-x[i])%mod*C(n*2-y[i]-z[i],n-y[i]))%mod;
printf("%d\n",ans);
return 0;
}