JZOJ5384. 【NOIP2017提高A组模拟9.23】四维世界 组合数学

来源:互联网 发布:pps网络电视播放器apk 编辑:程序博客网 时间:2024/05/21 06:30

题意:求从(0,0,0)到(n,n,n)的方案数,有m个障碍点。n<=1e5,m<=5e3.
强烈谴责出题人,tmd能不能更不负责一点,这题面明显就tmd是neerc的题目直接google翻译过来,居然还有不通的句子。
知道了题意这就是个水题。
由于n<=1e5,我们并不可以直接dp,所以转换思路。
像这种方格走的玩意儿,一般来说,如果不是dp,基本上就是排列组合没得跑。
明显正难则反,既然不能直接求,那就求不合法数量。
总数量很好求,每个方向n步,那么有Cn3nCn2n
问题是怎么求不合法。
其实如果不是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] (xixj+yiyj+zizj)!(xixj)!(yiyj)!(zizj)!
累死我了
代码打起来就很舒服了。

#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);}
原创粉丝点击