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

题意:求从(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);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值