题意:求从(0,0,0)到(n,n,n)的方案数,有m个障碍点。n<=1e5,m<=5e3.
强烈谴责出题人,tmd能不能更不负责一点,这题面明显就tmd是neerc的题目直接google翻译过来,居然还有不通的句子。
知道了题意这就是个水题。
由于n<=1e5,我们并不可以直接dp,所以转换思路。
像这种方格走的玩意儿,一般来说,如果不是dp,基本上就是排列组合没得跑。
明显正难则反,既然不能直接求,那就求不合法数量。
总数量很好求,每个方向n步,那么有
Cn3n∗Cn2n
问题是怎么求不合法。
其实如果不是m比较大我还是比较倾向于容斥。
设f[i]表示走到第i个不合法点且不经过其他不合法点的方案数。
那么答案就是f[m]。(把n,n,n加入进去)。
那么再套一个正难则反,f[i]=走到i禁止点的方案-经过禁止点的方案。
第一个同总方案,第二个的话我们考虑枚举。
走到i点经过j点,那么他的方案数肯定是走到j的方案+C(三个维度上能走的总步数,三维坐标差)。
那么具体的就有:
f[i]=
(xi+yi+zi)!xi!∗yi!∗zi!
−∑xj<=xi,yj<=yi,zj<=zif[j]∗
(xi−xj+yi−yj+zi−zj)!(xi−xj)!∗(yi−yj)!∗(zi−zj)!
累死我了
代码打起来就很舒服了。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=5e5+5;
const int mo=1e9+7;
typedef long long ll;
int fac[N],rev[N];
int f[N];
struct node
{
int x,y,z;
}a[N];
int n,m;
inline int pow(int a,int b)
{
int ret=1;
while (b)
{
if (b&1)ret=1ll*ret*a%mo;
a=1ll*a*a%mo;
b>>=1;
}
return ret;
}
bool cmp(node a,node b)
{
return a.x<b.x||(a.x==b.x&&a.y<b.y)||(a.x==b.x&&a.y==b.y&&a.z<b.z);
}
int main()
{
freopen("poly.in","r",stdin);
freopen("poly.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,m)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
a[++m].x=n,a[m].y=a[m].z=n;
fac[0]=1;
fo(i,1,3*n)fac[i]=1ll*fac[i-1]*i%mo;
rev[3*n]=pow(fac[3*n],mo-2);
fd(i,3*n,1)rev[i-1]=1ll*rev[i]*i%mo;
sort(a+1,a+1+m,cmp);
fo(i,1,m)
{
f[i]=1ll*fac[a[i].x+a[i].y+a[i].z]*rev[a[i].x]%mo*rev[a[i].y]%mo*rev[a[i].z]%mo;
int tmp=0;
fo(j,1,i-1)
if (a[i].x>=a[j].x&&a[i].y>=a[j].y&&a[i].z>=a[j].z)
{
if (a[i].x==a[j].x&&a[i].y==a[j].y&&a[i].z==a[j].z)printf("ERROR!\n");
tmp=(tmp+1ll*f[j]*fac[a[i].x-a[j].x+a[i].y-a[j].y+a[i].z-a[j].z]%mo*
rev[a[i].x-a[j].x]%mo*rev[a[i].y-a[j].y]%mo*rev[a[i].z-a[j].z]%mo)%mo;
}
f[i]=(f[i]-tmp+mo)%mo;
}
printf("%d\n",f[m]%mo);
}