【校内互侧】高维网络 (dp+组合数)

35 篇文章 0 订阅
5 篇文章 0 订阅

高维网络

【题目描述】
现在有一个 d 维的坐标网格,其中第 i 维坐标的范围是[0,a_i]。在这个范围内建立一个有向图:我们把范围内的每个整点(每一维坐标均为整数的点)当做图上的顶点。设点 A(0,0,⋯,0),B(a_1,a_2,⋯,a_d)。对于范围内的点(x_1,x_2,⋯,x_d),它会向以下这些点(如果目标点在范围内)连有向边:(x_1+1,x_2,⋯,x_d),(x_1,x_2+1,⋯,x_d),⋯,(x_1,x_2,⋯,x_d+1)
现在从点 A 到点 B 会有若干条路径,路径的条数可以十分简单地算出。然而不幸的是,范围内有 p 个点被破坏了(点 A 和点 B 不会被破坏) ,其中第 i个点的坐标为(x_(i,1),x_(i,2),⋯,x_(i,d))。你需要算出从点 A 到点B 剩余的路径条数。
由于答案可能很大,你只需要输出它对 1,000,000,007 取模的结果。
【输入格式】
第一行为两个整数 d,p。
第二行为 d 个整数,其中第 i 个数是 a_i。
接下来 p 行,每行 d 个整数,其中第 i 行第 j 个数是 x_(i,j)。
【输出格式】
一个整数,表示从点 A 到点 B 剩余的路径条数对 1,000,000,007 取模的结果。
【输入样例】
2 1
2 1
1 0
【输出样例】
1
【数据范围】
这里写图片描述
【题解】【dp+组合数+容斥原理】
【f[i]表示从A到i不经过被破坏的点的路径条数;g[i][j]表示从i到j可以经过的被破坏的点的方案数(可以用组合数求)】
【g[i][j]:(各维度权值之和的阶乘)/(各维度阶乘之积),如二维情况:(行数+列数)!/(行数!×列数!)】
【f[i]=总路径条数-不合法,f[x]=g[A][x]-Σf[y]*g[y][x]】
【最后输出f[B]】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int mod=1000000007;
struct bad{
    int x[110];
}a[510]; 
ll mi[10000010],sum[510],num[110],f[510],g[510][510],h[510];
int d,p;
int tmp(bad a,bad b)
{
    for(int i=1;i<=d;++i)
     {
        if(a.x[i]<b.x[i]) return 1;//刚开始写成 if(a.x[i]<b.x[i]) return 1; 
        if(a.x[i]>b.x[i]) return 0;  //           else return 0;  然后WA啊WA 
     }
} 
inline void jc()
{
    mi[0]=1;
    for(ll i=1;i<=10000000;++i)
     mi[i]=(ll)(mi[i-1]*i)%mod;
}
inline ll poww(ll x,ll num)
{
    ll ss=x%mod,ans=1;
    while(num)
     {
        if(num&1)
          ans=ans*ss%mod;
        num>>=1;
        ss=(ss*ss)%mod;
     }
    return ans%mod;
}
inline void slove()
{
    for(int i=1;i<=p;++i)
     {
        ll t=mi[sum[i]];
        ll t1=1;
        for(int j=1;j<=d;++j)
         if(a[i].x[j]) t1=(t1*mi[a[i].x[j]])%mod;
        ll ans=poww(t1,mod-2);
        g[0][i]=t*ans%mod;
     }
    for(int i=1;i<=p;++i)
     for(int j=1;j<=p;++j)
      if(i!=j)
       {
            ll tot=0; bool b=1;
            for(int k=1;k<=d;++k)
             {
                h[k]=a[j].x[k]-a[i].x[k],tot+=h[k];
                if(h[k]<0) {b=0; break;}
                }
            if(!b) continue;
            ll t=mi[tot],t1=1;
            for(int k=1;k<=d;++k)
             if(h[k]) t1=(t1*mi[h[k]])%mod;
            ll ans=poww(t1,mod-2);
            g[i][j]=t*ans%mod;
       }
    return;
}//预处理g[i][j] 
int main()
{
    freopen("cube.in","r",stdin);
    freopen("cube.out","w",stdout);
    int i,j;
    scanf("%d%d",&d,&p);
    for(i=1;i<=d;++i) scanf("%lld",&num[i]);
    for(i=1;i<=p;++i)
     for(j=1;j<=d;++j)
      scanf("%d",&a[i].x[j]);
    ++p;
    for(i=1;i<=d;++i) a[p].x[i]=num[i];
    sort(a+1,a+p+1,tmp);
    for(i=1;i<=p;++i)
     for(j=1;j<=d;++j)
      sum[i]+=a[i].x[j],sum[i]%=mod;
    jc();
    slove();
    for(i=1;i<=p;++i)
     {
        f[i]=g[0][i]%mod;
        for(j=1;j<i;++j)
         f[i]=(f[i]-f[j]*g[j][i])%mod;
        f[i]=(f[i]%mod+mod)%mod;
     }
    printf("%lld\n",f[p]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值